Merge "manifest_check.py: trim namespace part of the module, if needed."
diff --git a/android/bazel.go b/android/bazel.go
index 46212dc..d40e650 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -140,6 +140,7 @@
 
 		"prebuilts/sdk":/* recursive = */ false,
 		"prebuilts/sdk/tools":/* recursive = */ false,
+		"packages/apps/Music":/* recursive = */ false,
 	}
 
 	// Configure modules in these directories to enable bp2build_available: true or false by default.
diff --git a/android/module.go b/android/module.go
index 11f63bd..b40310b 100644
--- a/android/module.go
+++ b/android/module.go
@@ -413,6 +413,7 @@
 	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
+	InstallInVendor() bool
 	InstallBypassMake() bool
 	InstallForceOS() (*OsType, *ArchType)
 
@@ -471,6 +472,7 @@
 	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
+	InstallInVendor() bool
 	InstallBypassMake() bool
 	InstallForceOS() (*OsType, *ArchType)
 	HideFromMake()
@@ -1579,6 +1581,10 @@
 	return Bool(m.commonProperties.Recovery)
 }
 
+func (m *ModuleBase) InstallInVendor() bool {
+	return Bool(m.commonProperties.Vendor)
+}
+
 func (m *ModuleBase) InstallInRoot() bool {
 	return false
 }
@@ -2645,6 +2651,10 @@
 	return m.module.InstallForceOS()
 }
 
+func (m *moduleContext) InstallInVendor() bool {
+	return m.module.InstallInVendor()
+}
+
 func (m *moduleContext) skipInstall() bool {
 	if m.module.base().commonProperties.SkipInstall {
 		return true
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 422e46c..d6c7142 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4667,6 +4667,13 @@
 			prebuilt_bootclasspath_fragment {
 				name: "art-bootclasspath-fragment",
 				contents: ["core-oj"],
+				hidden_api: {
+					annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+					metadata: "my-bootclasspath-fragment/metadata.csv",
+					index: "my-bootclasspath-fragment/index.csv",
+					stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+					all_flags: "my-bootclasspath-fragment/all-flags.csv",
+				},
 			}
 
 			java_import {
@@ -4890,7 +4897,7 @@
 		}
 	}
 
-	checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
+	checkHiddenAPIIndexFromClassesInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
 		t.Helper()
 		platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
 		var rule android.TestingBuildParams
@@ -4899,6 +4906,15 @@
 		java.CheckHiddenAPIRuleInputs(t, "intermediate index", expectedIntermediateInputs, rule)
 	}
 
+	checkHiddenAPIIndexFromFlagsInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
+		t.Helper()
+		platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
+		var rule android.TestingBuildParams
+
+		rule = platformBootclasspath.Output("hiddenapi-index.csv")
+		java.CheckHiddenAPIRuleInputs(t, "monolithic index", expectedIntermediateInputs, rule)
+	}
+
 	fragment := java.ApexVariantReference{
 		Apex:   proptools.StringPtr("myapex"),
 		Module: proptools.StringPtr("my-bootclasspath-fragment"),
@@ -4923,6 +4939,13 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
@@ -4946,9 +4969,10 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexInputs(t, ctx, `
-			out/soong/.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
-			out/soong/.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
+			my-bootclasspath-fragment/index.csv
+			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 		`)
 	})
 
@@ -4964,6 +4988,13 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
@@ -4987,9 +5018,10 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexInputs(t, ctx, `
-			out/soong/.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
-			out/soong/.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
+			my-bootclasspath-fragment/index.csv
+			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 		`)
 	})
 
@@ -5012,6 +5044,13 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
@@ -5070,6 +5109,13 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
@@ -5108,9 +5154,10 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexInputs(t, ctx, `
-			out/soong/.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
-			out/soong/.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
+			my-bootclasspath-fragment/index.csv
+			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 		`)
 	})
 
@@ -5146,6 +5193,13 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
@@ -5182,9 +5236,10 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexInputs(t, ctx, `
-			out/soong/.intermediates/libbar/android_common_myapex/javac/libbar.jar
-			out/soong/.intermediates/libfoo/android_common_apex10000/javac/libfoo.jar
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
+			my-bootclasspath-fragment/index.csv
+			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 		`)
 	})
 
@@ -5220,6 +5275,13 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
@@ -5258,9 +5320,10 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexInputs(t, ctx, `
-			out/soong/.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
-			out/soong/.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
+			my-bootclasspath-fragment/index.csv
+			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 		`)
 	})
 }
@@ -7183,6 +7246,13 @@
 				name: "my-bootclasspath-fragment",
 				contents: ["libfoo"],
 				apex_available: ["myapex"],
+				hidden_api: {
+					annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+					metadata: "my-bootclasspath-fragment/metadata.csv",
+					index: "my-bootclasspath-fragment/index.csv",
+					stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+					all_flags: "my-bootclasspath-fragment/all-flags.csv",
+				},
 			}
 
 			java_import {
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 4b1600e..5cd3eab 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -383,6 +383,13 @@
 				apex_available: [
 					"com.android.art",
 				],
+				hidden_api: {
+					annotation_flags: "mybootclasspathfragment/annotation-flags.csv",
+					metadata: "mybootclasspathfragment/metadata.csv",
+					index: "mybootclasspathfragment/index.csv",
+					stub_flags: "mybootclasspathfragment/stub-flags.csv",
+					all_flags: "mybootclasspathfragment/all-flags.csv",
+				},
 			}
 		`, contentsInsert(contents), prefer)
 		return android.FixtureAddTextFile("prebuilts/module_sdk/art/Android.bp", text)
@@ -582,6 +589,13 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				annotation_flags: "mybootclasspathfragment/annotation-flags.csv",
+				metadata: "mybootclasspathfragment/metadata.csv",
+				index: "mybootclasspathfragment/index.csv",
+				stub_flags: "mybootclasspathfragment/stub-flags.csv",
+				all_flags: "mybootclasspathfragment/all-flags.csv",
+			},
 		}
 	`)
 
diff --git a/apex/builder.go b/apex/builder.go
index 24c049b..148f42f 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -761,7 +761,7 @@
 	rule := java.Signapk
 	args := map[string]string{
 		"certificates": pem.String() + " " + key.String(),
-		"flags":        "-a 4096", //alignment
+		"flags":        "-a 4096 --align-file-size", //alignment
 	}
 	implicits := android.Paths{pem, key}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
diff --git a/cc/Android.bp b/cc/Android.bp
index 4b750ff..164d32b 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -14,6 +14,7 @@
         "soong-cc-config",
         "soong-etc",
         "soong-genrule",
+        "soong-snapshot",
         "soong-tradefed",
     ],
     srcs: [
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 536f112..484e922 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -72,36 +72,34 @@
 				allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
 				allDeps = append(allDeps, baseLinkerProps.Shared_libs...)
 				allDeps = append(allDeps, baseLinkerProps.Exclude_shared_libs...)
+				allDeps = append(allDeps, baseLinkerProps.System_shared_libs...)
 			}
 		}
 	}
 
 	// Deps in the static: { .. } and shared: { .. } props of a cc_library.
 	if lib, ok := module.compiler.(*libraryDecorator); ok {
-		appendDeps := func(deps []string, p StaticOrSharedProperties) []string {
+		appendDeps := func(deps []string, p StaticOrSharedProperties, system bool) []string {
 			deps = append(deps, p.Static_libs...)
 			deps = append(deps, p.Whole_static_libs...)
 			deps = append(deps, p.Shared_libs...)
+			// TODO(b/186024507, b/186489250): Temporarily exclude adding
+			// system_shared_libs deps until libc and libm builds.
+			if system {
+				allDeps = append(allDeps, p.System_shared_libs...)
+			}
 			return deps
 		}
 
-		allDeps = appendDeps(allDeps, lib.SharedProperties.Shared)
-		allDeps = appendDeps(allDeps, lib.StaticProperties.Static)
-
-		// TODO(b/186024507, b/186489250): Temporarily exclude adding
-		// system_shared_libs deps until libc and libm builds.
-		if lib.static() {
-			allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...)
-		} else if lib.shared() {
-			allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...)
-		}
+		allDeps = appendDeps(allDeps, lib.SharedProperties.Shared, lib.shared())
+		allDeps = appendDeps(allDeps, lib.StaticProperties.Static, lib.static())
 
 		// Deps in the target/arch nested static: { .. } and shared: { .. } props of a cc_library.
 		// target: { <target>: shared: { ... } }
 		for _, configToProps := range module.GetArchVariantProperties(ctx, &SharedProperties{}) {
 			for _, props := range configToProps {
 				if p, ok := props.(*SharedProperties); ok {
-					allDeps = appendDeps(allDeps, p.Shared)
+					allDeps = appendDeps(allDeps, p.Shared, lib.shared())
 				}
 			}
 		}
@@ -109,7 +107,7 @@
 		for _, configToProps := range module.GetArchVariantProperties(ctx, &StaticProperties{}) {
 			for _, props := range configToProps {
 				if p, ok := props.(*StaticProperties); ok {
-					allDeps = appendDeps(allDeps, p.Static)
+					allDeps = appendDeps(allDeps, p.Static, lib.static())
 				}
 			}
 		}
diff --git a/cc/builder.go b/cc/builder.go
index b0842ec..842ce85 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -237,7 +237,7 @@
 	// -w has been added since header-abi-dumper does not need to produce any sort of diagnostic information.
 	sAbiDump, sAbiDumpRE = pctx.RemoteStaticRules("sAbiDump",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && $reTemplate$sAbiDumper -o ${out} $in $exportDirs -- $cFlags -w -isystem prebuilts/clang-tools/${config.HostPrebuiltTag}/clang-headers",
+			Command:     "rm -f $out && $reTemplate$sAbiDumper --root-dir . --root-dir $$OUT_DIR:out -o ${out} $in $exportDirs -- $cFlags -w -isystem prebuilts/clang-tools/${config.HostPrebuiltTag}/clang-headers",
 			CommandDeps: []string{"$sAbiDumper"},
 		}, &remoteexec.REParams{
 			Labels:       map[string]string{"type": "abi-dump", "tool": "header-abi-dumper"},
@@ -255,7 +255,7 @@
 	// sAbi dump file.
 	sAbiLink, sAbiLinkRE = pctx.RemoteStaticRules("sAbiLink",
 		blueprint.RuleParams{
-			Command:        "$reTemplate$sAbiLinker -o ${out} $symbolFilter -arch $arch  $exportedHeaderFlags @${out}.rsp ",
+			Command:        "$reTemplate$sAbiLinker --root-dir . --root-dir $$OUT_DIR:out -o ${out} $symbolFilter -arch $arch $exportedHeaderFlags @${out}.rsp",
 			CommandDeps:    []string{"$sAbiLinker"},
 			Rspfile:        "${out}.rsp",
 			RspfileContent: "${in}",
diff --git a/cc/genrule.go b/cc/genrule.go
index b0efc6c..0ca901e 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"android/soong/genrule"
+	"android/soong/snapshot"
 )
 
 func init() {
@@ -84,7 +85,7 @@
 	// is not needed.
 	recoverySnapshotVersion := ctx.DeviceConfig().RecoverySnapshotVersion()
 	if recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" &&
-		!isRecoveryProprietaryModule(ctx) {
+		!snapshot.IsRecoveryProprietaryModule(ctx) {
 		return false
 	} else {
 		return Bool(g.Recovery_available)
@@ -103,7 +104,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" || !IsVendorProprietaryModule(ctx) {
+		if vndkVersion == "current" || !snapshot.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
index 15ec1c8..3a0857b 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -22,6 +22,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/snapshot"
 )
 
 var _ android.ImageInterface = (*Module)(nil)
@@ -496,7 +497,7 @@
 		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
 		// PLATFORM_VNDK_VERSION.
 		if m.HasVendorVariant() {
-			if IsVendorProprietaryModule(mctx) {
+			if snapshot.IsVendorProprietaryModule(mctx) {
 				vendorVariants = append(vendorVariants, boardVndkVersion)
 			} else {
 				vendorVariants = append(vendorVariants, platformVndkVersion)
@@ -525,7 +526,7 @@
 				platformVndkVersion,
 				boardVndkVersion,
 			)
-		} else if IsVendorProprietaryModule(mctx) {
+		} else if snapshot.IsVendorProprietaryModule(mctx) {
 			vendorVariants = append(vendorVariants, boardVndkVersion)
 		} else {
 			vendorVariants = append(vendorVariants, platformVndkVersion)
@@ -582,7 +583,7 @@
 	if !m.KernelHeadersDecorator() &&
 		!m.IsSnapshotPrebuilt() &&
 		usingRecoverySnapshot &&
-		!isRecoveryProprietaryModule(mctx) {
+		!snapshot.IsRecoveryProprietaryModule(mctx) {
 		recoveryVariantNeeded = false
 	}
 
diff --git a/cc/linkable.go b/cc/linkable.go
index 6232efb..b510508 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -3,6 +3,7 @@
 import (
 	"android/soong/android"
 	"android/soong/bazel/cquery"
+	"android/soong/snapshot"
 
 	"github.com/google/blueprint"
 )
@@ -71,15 +72,12 @@
 
 // Snapshottable defines those functions necessary for handling module snapshots.
 type Snapshottable interface {
+	snapshot.VendorSnapshotModuleInterface
+	snapshot.RecoverySnapshotModuleInterface
+
 	// SnapshotHeaders returns a list of header paths provided by this module.
 	SnapshotHeaders() android.Paths
 
-	// ExcludeFromVendorSnapshot returns true if this module should be otherwise excluded from the vendor snapshot.
-	ExcludeFromVendorSnapshot() bool
-
-	// ExcludeFromRecoverySnapshot returns true if this module should be otherwise excluded from the recovery snapshot.
-	ExcludeFromRecoverySnapshot() bool
-
 	// SnapshotLibrary returns true if this module is a snapshot library.
 	IsSnapshotLibrary() bool
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index defe8fd..3863c3a 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -25,6 +25,7 @@
 
 	"android/soong/android"
 	"android/soong/cc/config"
+	"android/soong/snapshot"
 )
 
 var (
@@ -907,7 +908,7 @@
 // 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) {
+	if snapshot.IsVendorProprietaryModule(mctx) {
 		return false
 	}
 
diff --git a/cc/sdk.go b/cc/sdk.go
index 69ad311..a83e5ad 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -76,7 +76,7 @@
 			}
 			ctx.AliasVariation("")
 		}
-	case *snapshot:
+	case *snapshotModule:
 		ctx.CreateVariations("")
 	}
 }
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 4f031ff..9672c0f 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -18,57 +18,17 @@
 // snapshot mutators and snapshot information maps which are also defined in this file.
 
 import (
-	"path/filepath"
 	"strings"
 
 	"android/soong/android"
+	"android/soong/snapshot"
 
 	"github.com/google/blueprint"
 )
 
-// Defines the specifics of different images to which the snapshot process is applicable, e.g.,
-// vendor, recovery, ramdisk.
+// This interface overrides snapshot.SnapshotImage to implement cc module specific functions
 type SnapshotImage interface {
-	// 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 LinkableInterface) func() bool
-
-	// Returns true if the module is private and must not be included in the
-	// snapshot. For example VNDK-private modules must return true for the
-	// vendor snapshots. But false for the recovery snapshots.
-	private(m LinkableInterface) 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 LinkableInterface) bool
-
-	// Returns true if the build is using a snapshot for this image.
-	isUsingSnapshot(cfg android.DeviceConfig) bool
-
-	// Returns a version of which the snapshot should be used in this target.
-	// This will only be meaningful when isUsingSnapshot is true.
-	targetSnapshotVersion(cfg android.DeviceConfig) string
-
-	// 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
+	snapshot.SnapshotImage
 
 	// The image variant name for this snapshot image.
 	// For example, recovery snapshot image will return "recovery", and vendor snapshot image will
@@ -80,110 +40,12 @@
 	moduleNameSuffix() string
 }
 
-type vendorSnapshotImage struct{}
-type recoverySnapshotImage 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,
-	}
-
-	// 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(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
-	ctx.RegisterModuleType("vendor_snapshot", vendorSnapshotFactory)
-	ctx.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
-	ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
-	ctx.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
-	ctx.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
-	ctx.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
-
-	ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+type vendorSnapshotImage struct {
+	*snapshot.VendorSnapshotImage
 }
 
-func (vendorSnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
-	ctx.RegisterModuleType(name, factory)
-}
-
-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 LinkableInterface) func() bool {
-	return m.InVendor
-}
-
-func (vendorSnapshotImage) private(m LinkableInterface) bool {
-	return m.IsVndkPrivate()
-}
-
-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 LinkableInterface) bool {
-	return m.ExcludeFromVendorSnapshot()
-}
-
-func (vendorSnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
-	vndkVersion := cfg.VndkVersion()
-	return vndkVersion != "current" && vndkVersion != ""
-}
-
-func (vendorSnapshotImage) targetSnapshotVersion(cfg android.DeviceConfig) string {
-	return cfg.VndkVersion()
-}
-
-// 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]
+type recoverySnapshotImage struct {
+	*snapshot.RecoverySnapshotImage
 }
 
 func (vendorSnapshotImage) imageVariantName(cfg android.DeviceConfig) string {
@@ -194,62 +56,6 @@
 	return VendorSuffix
 }
 
-func (recoverySnapshotImage) init(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
-	ctx.RegisterModuleType("recovery_snapshot", recoverySnapshotFactory)
-	ctx.RegisterModuleType("recovery_snapshot_shared", RecoverySnapshotSharedFactory)
-	ctx.RegisterModuleType("recovery_snapshot_static", RecoverySnapshotStaticFactory)
-	ctx.RegisterModuleType("recovery_snapshot_header", RecoverySnapshotHeaderFactory)
-	ctx.RegisterModuleType("recovery_snapshot_binary", RecoverySnapshotBinaryFactory)
-	ctx.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 LinkableInterface) func() bool {
-	return m.InRecovery
-}
-
-// recovery snapshot does not have private libraries.
-func (recoverySnapshotImage) private(m LinkableInterface) bool {
-	return false
-}
-
-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 LinkableInterface) bool {
-	return m.ExcludeFromRecoverySnapshot()
-}
-
-func (recoverySnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
-	recoverySnapshotVersion := cfg.RecoverySnapshotVersion()
-	return recoverySnapshotVersion != "current" && recoverySnapshotVersion != ""
-}
-
-func (recoverySnapshotImage) targetSnapshotVersion(cfg android.DeviceConfig) string {
-	return cfg.RecoverySnapshotVersion()
-}
-
-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 (recoverySnapshotImage) imageVariantName(cfg android.DeviceConfig) string {
 	return android.RecoveryVariation
 }
@@ -258,12 +64,31 @@
 	return recoverySuffix
 }
 
-var VendorSnapshotImageSingleton vendorSnapshotImage
-var recoverySnapshotImageSingleton recoverySnapshotImage
+// Override existing vendor and recovery snapshot for cc module specific extra functions
+var VendorSnapshotImageSingleton vendorSnapshotImage = vendorSnapshotImage{&snapshot.VendorSnapshotImageSingleton}
+var recoverySnapshotImageSingleton recoverySnapshotImage = recoverySnapshotImage{&snapshot.RecoverySnapshotImageSingleton}
+
+func RegisterVendorSnapshotModules(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("vendor_snapshot", vendorSnapshotFactory)
+	ctx.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
+	ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
+	ctx.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
+	ctx.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
+	ctx.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
+}
+
+func RegisterRecoverySnapshotModules(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("recovery_snapshot", recoverySnapshotFactory)
+	ctx.RegisterModuleType("recovery_snapshot_shared", RecoverySnapshotSharedFactory)
+	ctx.RegisterModuleType("recovery_snapshot_static", RecoverySnapshotStaticFactory)
+	ctx.RegisterModuleType("recovery_snapshot_header", RecoverySnapshotHeaderFactory)
+	ctx.RegisterModuleType("recovery_snapshot_binary", RecoverySnapshotBinaryFactory)
+	ctx.RegisterModuleType("recovery_snapshot_object", RecoverySnapshotObjectFactory)
+}
 
 func init() {
-	VendorSnapshotImageSingleton.Init(android.InitRegistrationContext)
-	recoverySnapshotImageSingleton.init(android.InitRegistrationContext)
+	RegisterVendorSnapshotModules(android.InitRegistrationContext)
+	RegisterRecoverySnapshotModules(android.InitRegistrationContext)
 	android.RegisterMakeVarsProvider(pctx, snapshotMakeVarsProvider)
 }
 
@@ -285,8 +110,7 @@
 	Binaries    []string `android:"arch_variant"`
 	Objects     []string `android:"arch_variant"`
 }
-
-type snapshot struct {
+type snapshotModule struct {
 	android.ModuleBase
 
 	properties SnapshotProperties
@@ -296,41 +120,41 @@
 	image SnapshotImage
 }
 
-func (s *snapshot) ImageMutatorBegin(ctx android.BaseModuleContext) {
+func (s *snapshotModule) ImageMutatorBegin(ctx android.BaseModuleContext) {
 	cfg := ctx.DeviceConfig()
-	if !s.image.isUsingSnapshot(cfg) || s.image.targetSnapshotVersion(cfg) != s.baseSnapshot.Version() {
+	if !s.image.IsUsingSnapshot(cfg) || s.image.TargetSnapshotVersion(cfg) != s.baseSnapshot.Version() {
 		s.Disable()
 	}
 }
 
-func (s *snapshot) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *snapshotModule) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
 
-func (s *snapshot) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *snapshotModule) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
 
-func (s *snapshot) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *snapshotModule) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
 
-func (s *snapshot) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *snapshotModule) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
 
-func (s *snapshot) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *snapshotModule) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
 
-func (s *snapshot) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+func (s *snapshotModule) ExtraImageVariations(ctx android.BaseModuleContext) []string {
 	return []string{s.image.imageVariantName(ctx.DeviceConfig())}
 }
 
-func (s *snapshot) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+func (s *snapshotModule) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
 }
 
-func (s *snapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (s *snapshotModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Nothing, the snapshot module is only used to forward dependency information in DepsMutator.
 }
 
@@ -342,7 +166,7 @@
 	return moduleSuffix + versionSuffix
 }
 
-func (s *snapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
+func (s *snapshotModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	collectSnapshotMap := func(names []string, snapshotSuffix, moduleSuffix string) map[string]string {
 		snapshotMap := make(map[string]string)
 		for _, name := range names {
@@ -382,12 +206,12 @@
 
 var SnapshotInfoProvider = blueprint.NewMutatorProvider(SnapshotInfo{}, "deps")
 
-var _ android.ImageInterface = (*snapshot)(nil)
+var _ android.ImageInterface = (*snapshotModule)(nil)
 
 func snapshotMakeVarsProvider(ctx android.MakeVarsContext) {
 	snapshotSet := map[string]struct{}{}
 	ctx.VisitAllModules(func(m android.Module) {
-		if s, ok := m.(*snapshot); ok {
+		if s, ok := m.(*snapshotModule); ok {
 			if _, ok := snapshotSet[s.Name()]; ok {
 				// arch variant generates duplicated modules
 				// skip this as we only need to know the path of the module.
@@ -411,13 +235,13 @@
 }
 
 func snapshotFactory(image SnapshotImage) android.Module {
-	snapshot := &snapshot{}
-	snapshot.image = image
-	snapshot.AddProperties(
-		&snapshot.properties,
-		&snapshot.baseSnapshot.baseProperties)
-	android.InitAndroidArchModule(snapshot, android.DeviceSupported, android.MultilibBoth)
-	return snapshot
+	snapshotModule := &snapshotModule{}
+	snapshotModule.image = image
+	snapshotModule.AddProperties(
+		&snapshotModule.properties,
+		&snapshotModule.baseSnapshot.baseProperties)
+	android.InitAndroidArchModule(snapshotModule, android.DeviceSupported, android.MultilibBoth)
+	return snapshotModule
 }
 
 type BaseSnapshotDecoratorProperties struct {
@@ -449,7 +273,7 @@
 // will be seen as "libbase.vendor_static.30.arm64" by Soong.
 type BaseSnapshotDecorator struct {
 	baseProperties BaseSnapshotDecoratorProperties
-	image          SnapshotImage
+	Image          SnapshotImage
 }
 
 func (p *BaseSnapshotDecorator) Name(name string) string {
@@ -489,7 +313,7 @@
 		Variation: android.CoreVariation})
 
 	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(LinkableInterface).BaseModuleName()) {
-		p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
+		p.baseProperties.Androidmk_suffix = p.Image.moduleNameSuffix()
 		return
 	}
 
@@ -498,14 +322,14 @@
 		Variation: ProductVariationPrefix + ctx.DeviceConfig().PlatformVndkVersion()})
 
 	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(LinkableInterface).BaseModuleName()) {
-		p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
+		p.baseProperties.Androidmk_suffix = p.Image.moduleNameSuffix()
 		return
 	}
 
 	images := []SnapshotImage{VendorSnapshotImageSingleton, recoverySnapshotImageSingleton}
 
 	for _, image := range images {
-		if p.image == image {
+		if p.Image == image {
 			continue
 		}
 		variations = append(ctx.Target().Variations(), blueprint.Variation{
@@ -518,7 +342,7 @@
 					image.moduleNameSuffix()+variant,
 					p.Version(),
 					ctx.DeviceConfig().Arches()[0].ArchType.String())) {
-			p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
+			p.baseProperties.Androidmk_suffix = p.Image.moduleNameSuffix()
 			return
 		}
 	}
@@ -529,7 +353,7 @@
 // Call this with a module suffix after creating a snapshot module, such as
 // vendorSnapshotSharedSuffix, recoverySnapshotBinarySuffix, etc.
 func (p *BaseSnapshotDecorator) Init(m LinkableInterface, image SnapshotImage, moduleSuffix string) {
-	p.image = image
+	p.Image = image
 	p.baseProperties.ModuleSuffix = image.moduleNameSuffix() + moduleSuffix
 	m.AddProperties(&p.baseProperties)
 	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index b0538be..24abcce 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -114,7 +114,7 @@
 	}
 
 	for _, image := range []SnapshotImage{VendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
-		if isSnapshotAware(ctx.DeviceConfig(), m, image.isProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()), apexInfo, image) {
+		if isSnapshotAware(ctx.DeviceConfig(), m, image.IsProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()), apexInfo, image) {
 			return true
 		}
 	}
diff --git a/cc/testing.go b/cc/testing.go
index b9d84f6..6a002c0 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -20,6 +20,7 @@
 
 	"android/soong/android"
 	"android/soong/genrule"
+	"android/soong/snapshot"
 )
 
 func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
@@ -638,8 +639,10 @@
 var PrepareForTestWithCcIncludeVndk = android.GroupFixturePreparers(
 	PrepareForIntegrationTestWithCc,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		VendorSnapshotImageSingleton.Init(ctx)
-		recoverySnapshotImageSingleton.init(ctx)
+		snapshot.VendorSnapshotImageSingleton.Init(ctx)
+		snapshot.RecoverySnapshotImageSingleton.Init(ctx)
+		RegisterVendorSnapshotModules(ctx)
+		RegisterRecoverySnapshotModules(ctx)
 		ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 	}),
 )
@@ -687,8 +690,10 @@
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 
-	VendorSnapshotImageSingleton.Init(ctx)
-	recoverySnapshotImageSingleton.init(ctx)
+	snapshot.VendorSnapshotImageSingleton.Init(ctx)
+	snapshot.RecoverySnapshotImageSingleton.Init(ctx)
+	RegisterVendorSnapshotModules(ctx)
+	RegisterRecoverySnapshotModules(ctx)
 	ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 	RegisterVndkLibraryTxtTypes(ctx)
 
diff --git a/cc/util.go b/cc/util.go
index 1220d84..9bba876 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -21,6 +21,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/snapshot"
 )
 
 // Efficiently converts a list of include directories to a single string
@@ -126,20 +127,6 @@
 		"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: "copy " + path.String() + " -> " + 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{
@@ -151,12 +138,6 @@
 	return outPath
 }
 
-func writeStringToFileRule(ctx android.SingletonContext, content, out string) android.OutputPath {
-	outPath := android.PathForOutput(ctx, out)
-	android.WriteFileRule(ctx, outPath, content)
-	return outPath
-}
-
 // Dump a map to a list file as:
 //
 // {key1} {value1}
@@ -172,5 +153,5 @@
 		txtBuilder.WriteString(" ")
 		txtBuilder.WriteString(m[k])
 	}
-	return writeStringToFileRule(ctx, txtBuilder.String(), path)
+	return snapshot.WriteStringToFileRule(ctx, txtBuilder.String(), path)
 }
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 003b7c9..ba4d79f 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -13,141 +13,46 @@
 // limitations under the License.
 package cc
 
-// This file contains singletons to capture vendor and recovery snapshot. They consist of prebuilt
-// modules under AOSP so older vendor and recovery can be built with a newer system in a single
-// source tree.
-
 import (
 	"encoding/json"
 	"path/filepath"
-	"sort"
 	"strings"
 
 	"android/soong/android"
+	"android/soong/snapshot"
 )
 
-var vendorSnapshotSingleton = snapshotSingleton{
-	"vendor",
-	"SOONG_VENDOR_SNAPSHOT_ZIP",
-	android.OptionalPath{},
-	true,
-	VendorSnapshotImageSingleton,
-	false, /* fake */
-}
+// This file defines how to capture cc modules into snapshot package.
 
-var vendorFakeSnapshotSingleton = snapshotSingleton{
-	"vendor",
-	"SOONG_VENDOR_FAKE_SNAPSHOT_ZIP",
-	android.OptionalPath{},
-	true,
-	VendorSnapshotImageSingleton,
-	true, /* fake */
-}
-
-var recoverySnapshotSingleton = snapshotSingleton{
-	"recovery",
-	"SOONG_RECOVERY_SNAPSHOT_ZIP",
-	android.OptionalPath{},
-	false,
-	recoverySnapshotImageSingleton,
-	false, /* fake */
-}
-
-func VendorSnapshotSingleton() android.Singleton {
-	return &vendorSnapshotSingleton
-}
-
-func VendorFakeSnapshotSingleton() android.Singleton {
-	return &vendorFakeSnapshotSingleton
-}
-
-func RecoverySnapshotSingleton() android.Singleton {
-	return &recoverySnapshotSingleton
-}
-
-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
-}
-
-// 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)
-}
-
-func IsVendorProprietaryModule(ctx android.BaseModuleContext) bool {
-	// Any module in a vendor proprietary path is a vendor proprietary
-	// module.
-	if isVendorProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
+// Checks if the target image would contain VNDK
+func includeVndk(image snapshot.SnapshotImage) bool {
+	if image.ImageName() == snapshot.VendorSnapshotImageName {
 		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().(LinkableInterface); ok {
-		if c.ExcludeFromVendorSnapshot() {
-			return true
-		}
-	}
-
 	return false
 }
 
-func isRecoveryProprietaryModule(ctx android.BaseModuleContext) bool {
-
-	// Any module in a recovery proprietary path is a recovery proprietary
-	// module.
-	if isRecoveryProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
+// Check if the module is VNDK private
+func isPrivate(image snapshot.SnapshotImage, m LinkableInterface) bool {
+	if image.ImageName() == snapshot.VendorSnapshotImageName && m.IsVndkPrivate() {
 		return true
 	}
 
-	// However if the module is not in a recovery proprietary path, it may
-	// still be a recovery proprietary module. This happens for cc modules
-	// that are excluded from the recovery snapshot, and it means that the
-	// vendor has assumed control of the framework-provided module.
+	return false
+}
 
-	if c, ok := ctx.Module().(LinkableInterface); ok {
-		if c.ExcludeFromRecoverySnapshot() {
-			return true
-		}
+// Checks if target image supports VNDK Ext
+func supportsVndkExt(image snapshot.SnapshotImage) bool {
+	if image.ImageName() == snapshot.VendorSnapshotImageName {
+		return true
 	}
 
 	return false
 }
 
 // Determines if the module is a candidate for snapshot.
-func isSnapshotAware(cfg android.DeviceConfig, m LinkableInterface, inProprietaryPath bool, apexInfo android.ApexInfo, image SnapshotImage) bool {
+func isSnapshotAware(cfg android.DeviceConfig, m LinkableInterface, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshot.SnapshotImage) bool {
 	if !m.Enabled() || m.HiddenFromMake() {
 		return false
 	}
@@ -158,12 +63,12 @@
 	}
 	// skip proprietary modules, but (for the vendor snapshot only)
 	// include all VNDK (static)
-	if inProprietaryPath && (!image.includeVndk() || !m.IsVndk()) {
+	if inProprietaryPath && (!includeVndk(image) || !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) {
+	if image.ExcludeFromSnapshot(m) {
 		return false
 	}
 	if m.Target().Os.Class != android.Device {
@@ -173,7 +78,7 @@
 		return false
 	}
 	// the module must be installed in target image
-	if !apexInfo.IsForPlatform() || m.IsSnapshotPrebuilt() || !image.inImage(m)() {
+	if !apexInfo.IsForPlatform() || m.IsSnapshotPrebuilt() || !image.InImage(m)() {
 		return false
 	}
 	// skip kernel_headers which always depend on vendor
@@ -203,13 +108,13 @@
 			}
 		}
 		if sanitizable.Static() {
-			return sanitizable.OutputFile().Valid() && !image.private(m)
+			return sanitizable.OutputFile().Valid() && !isPrivate(image, m)
 		}
 		if sanitizable.Shared() || sanitizable.Rlib() {
 			if !sanitizable.OutputFile().Valid() {
 				return false
 			}
-			if image.includeVndk() {
+			if includeVndk(image) {
 				if !sanitizable.IsVndk() {
 					return true
 				}
@@ -256,15 +161,9 @@
 	VintfFragments []string `json:",omitempty"`
 }
 
-func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	if !c.image.shouldGenerateSnapshot(ctx) {
-		return
-	}
-
-	var snapshotOutputs android.Paths
-
+var ccSnapshotAction snapshot.GenerateSnapshotAction = func(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths {
 	/*
-		Vendor snapshot zipped artifacts directory structure:
+		Vendor snapshot zipped artifacts directory structure for cc modules:
 		{SNAPSHOT_ARCH}/
 			arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
 				shared/
@@ -296,13 +195,7 @@
 				(header files of same directory structure with source tree)
 	*/
 
-	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())
+	var snapshotOutputs android.Paths
 
 	includeDir := filepath.Join(snapshotArchDir, "include")
 	configsDir := filepath.Join(snapshotArchDir, "configs")
@@ -317,9 +210,9 @@
 		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)
+			return snapshot.WriteStringToFileRule(ctx, "", out)
 		} else {
-			return copyFileRule(ctx, path, out)
+			return snapshot.CopyFileRule(pctx, ctx, path, out)
 		}
 	}
 
@@ -337,7 +230,7 @@
 
 		// Common properties among snapshots.
 		prop.ModuleName = ctx.ModuleName(m)
-		if c.supportsVndkExt && m.IsVndkExt() {
+		if supportsVndkExt(s.Image) && m.IsVndkExt() {
 			// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
 			if m.IsVndkSp() {
 				prop.RelativeInstallPath = "vndk-sp"
@@ -457,7 +350,7 @@
 			ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
 			return nil
 		}
-		ret = append(ret, writeStringToFileRule(ctx, string(j), propOut))
+		ret = append(ret, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
 
 		return ret
 	}
@@ -469,10 +362,10 @@
 		}
 
 		moduleDir := ctx.ModuleDir(module)
-		inProprietaryPath := c.image.isProprietaryPath(moduleDir, ctx.DeviceConfig())
+		inProprietaryPath := s.Image.IsProprietaryPath(moduleDir, ctx.DeviceConfig())
 		apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
 
-		if c.image.excludeFromSnapshot(m) {
+		if s.Image.ExcludeFromSnapshot(m) {
 			if inProprietaryPath {
 				// Error: exclude_from_vendor_snapshot applies
 				// to framework-path modules only.
@@ -481,7 +374,7 @@
 			}
 		}
 
-		if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, c.image) {
+		if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, s.Image) {
 			return
 		}
 
@@ -489,8 +382,8 @@
 		// 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 := s.Fake
+		if s.Image.ExcludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) {
 			installAsFake = true
 		}
 
@@ -514,47 +407,12 @@
 
 	// install all headers after removing duplicates
 	for _, header := range android.FirstUniquePaths(headers) {
-		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), c.fake))
+		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), s.Fake))
 	}
 
-	// All artifacts are ready. Sort them to normalize ninja and then zip.
-	sort.Slice(snapshotOutputs, func(i, j int) bool {
-		return snapshotOutputs[i].String() < snapshotOutputs[j].String()
-	})
-
-	zipPath := android.PathForOutput(
-		ctx,
-		snapshotDir,
-		c.name+"-"+ctx.Config().DeviceName()+".zip")
-	zipRule := android.NewRuleBuilder(pctx, ctx)
-
-	// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
-	snapshotOutputList := android.PathForOutput(
-		ctx,
-		snapshotDir,
-		c.name+"-"+ctx.Config().DeviceName()+"_list")
-	rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp")
-	zipRule.Command().
-		Text("tr").
-		FlagWithArg("-d ", "\\'").
-		FlagWithRspFileInputList("< ", rspFile, snapshotOutputs).
-		FlagWithOutput("> ", snapshotOutputList)
-
-	zipRule.Temporary(snapshotOutputList)
-
-	zipRule.Command().
-		BuiltTool("soong_zip").
-		FlagWithOutput("-o ", zipPath).
-		FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
-		FlagWithInput("-l ", snapshotOutputList)
-
-	zipRule.Build(zipPath.String(), c.name+" snapshot "+zipPath.String())
-	zipRule.DeleteTemporaryFiles()
-	c.snapshotZipFile = android.OptionalPathForPath(zipPath)
+	return snapshotOutputs
 }
 
-func (c *snapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.Strict(
-		c.makeVar,
-		c.snapshotZipFile.String())
+func init() {
+	snapshot.RegisterSnapshotAction(ccSnapshotAction)
 }
diff --git a/cc/vndk.go b/cc/vndk.go
index 499d428..1ae79de 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -25,6 +25,7 @@
 	"android/soong/android"
 	"android/soong/cc/config"
 	"android/soong/etc"
+	"android/soong/snapshot"
 
 	"github.com/google/blueprint"
 )
@@ -698,7 +699,7 @@
 
 		libPath := m.outputFile.Path()
 		snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "shared", vndkType, libPath.Base())
-		ret = append(ret, copyFileRule(ctx, libPath, snapshotLibOut))
+		ret = append(ret, snapshot.CopyFileRule(pctx, ctx, libPath, snapshotLibOut))
 
 		if ctx.Config().VndkSnapshotBuildArtifacts() {
 			prop := struct {
@@ -720,7 +721,7 @@
 				ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
 				return nil, false
 			}
-			ret = append(ret, writeStringToFileRule(ctx, string(j), propOut))
+			ret = append(ret, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
 		}
 		return ret, true
 	}
@@ -778,8 +779,8 @@
 
 	// install all headers after removing duplicates
 	for _, header := range android.FirstUniquePaths(headers) {
-		snapshotOutputs = append(snapshotOutputs, copyFileRule(
-			ctx, header, filepath.Join(includeDir, header.String())))
+		snapshotOutputs = append(snapshotOutputs, snapshot.CopyFileRule(
+			pctx, ctx, header, filepath.Join(includeDir, header.String())))
 	}
 
 	// install *.libraries.txt except vndkcorevariant.libraries.txt
@@ -788,8 +789,8 @@
 		if !ok || !m.Enabled() || m.Name() == vndkUsingCoreVariantLibrariesTxt {
 			return
 		}
-		snapshotOutputs = append(snapshotOutputs, copyFileRule(
-			ctx, m.OutputFile(), filepath.Join(configsDir, m.Name())))
+		snapshotOutputs = append(snapshotOutputs, snapshot.CopyFileRule(
+			pctx, ctx, m.OutputFile(), filepath.Join(configsDir, m.Name())))
 	})
 
 	/*
diff --git a/etc/Android.bp b/etc/Android.bp
index cab7389..06a2fa1 100644
--- a/etc/Android.bp
+++ b/etc/Android.bp
@@ -9,6 +9,7 @@
         "blueprint",
         "soong",
         "soong-android",
+        "soong-snapshot",
     ],
     srcs: [
         "prebuilt_etc.go",
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 4dd383d..4107916 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -28,12 +28,15 @@
 // various `prebuilt_*` mutators.
 
 import (
+	"encoding/json"
 	"fmt"
+	"path/filepath"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/snapshot"
 )
 
 var pctx = android.NewPackageContext("android/soong/etc")
@@ -43,6 +46,7 @@
 func init() {
 	pctx.Import("android/soong/android")
 	RegisterPrebuiltEtcBuildComponents(android.InitRegistrationContext)
+	snapshot.RegisterSnapshotAction(generatePrebuiltSnapshot)
 }
 
 func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) {
@@ -128,6 +132,9 @@
 	android.ModuleBase
 	android.DefaultableModuleBase
 
+	snapshot.VendorSnapshotModuleInterface
+	snapshot.RecoverySnapshotModuleInterface
+
 	properties       prebuiltEtcProperties
 	subdirProperties prebuiltSubdirProperties
 
@@ -183,7 +190,7 @@
 	return p.inDebugRamdisk()
 }
 
-func (p *PrebuiltEtc) inRecovery() bool {
+func (p *PrebuiltEtc) InRecovery() bool {
 	return p.ModuleBase.InRecovery() || p.ModuleBase.InstallInRecovery()
 }
 
@@ -192,7 +199,7 @@
 }
 
 func (p *PrebuiltEtc) InstallInRecovery() bool {
-	return p.inRecovery()
+	return p.InRecovery()
 }
 
 var _ android.ImageInterface = (*PrebuiltEtc)(nil)
@@ -271,6 +278,18 @@
 	return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
 }
 
+func (p *PrebuiltEtc) InVendor() bool {
+	return p.ModuleBase.InstallInVendor()
+}
+
+func (p *PrebuiltEtc) ExcludeFromVendorSnapshot() bool {
+	return false
+}
+
+func (p *PrebuiltEtc) ExcludeFromRecoverySnapshot() bool {
+	return false
+}
+
 func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if p.properties.Src == nil {
 		ctx.PropertyErrorf("src", "missing prebuilt source file")
@@ -344,7 +363,7 @@
 	if p.inDebugRamdisk() && !p.onlyInDebugRamdisk() {
 		nameSuffix = ".debug_ramdisk"
 	}
-	if p.inRecovery() && !p.onlyInRecovery() {
+	if p.InRecovery() && !p.onlyInRecovery() {
 		nameSuffix = ".recovery"
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
@@ -494,3 +513,137 @@
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
 	return module
 }
+
+// Flags to be included in the snapshot
+type snapshotJsonFlags struct {
+	ModuleName          string `json:",omitempty"`
+	Filename            string `json:",omitempty"`
+	RelativeInstallPath string `json:",omitempty"`
+}
+
+// Copy file into the snapshot
+func copyFile(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath {
+	if fake {
+		// Create empty file instead for the fake snapshot
+		return snapshot.WriteStringToFileRule(ctx, "", out)
+	} else {
+		return snapshot.CopyFileRule(pctx, ctx, path, out)
+	}
+}
+
+// Check if the module is target of the snapshot
+func isSnapshotAware(ctx android.SingletonContext, m *PrebuiltEtc, image snapshot.SnapshotImage) bool {
+	if !m.Enabled() {
+		return false
+	}
+
+	// Skip if the module is not included in the image
+	if !image.InImage(m)() {
+		return false
+	}
+
+	// When android/prebuilt.go selects between source and prebuilt, it sets
+	// HideFromMake on the other one to avoid duplicate install rules in make.
+	if m.IsHideFromMake() {
+		return false
+	}
+
+	// There are some prebuilt_etc module with multiple definition of same name.
+	// Check if the target would be included from the build
+	if !m.ExportedToMake() {
+		return false
+	}
+
+	// Skip if the module is in the predefined path list to skip
+	if image.IsProprietaryPath(ctx.ModuleDir(m), ctx.DeviceConfig()) {
+		return false
+	}
+
+	// Skip if the module should be excluded
+	if image.ExcludeFromSnapshot(m) || image.ExcludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) {
+		return false
+	}
+
+	// Skip from other exceptional cases
+	if m.Target().Os.Class != android.Device {
+		return false
+	}
+	if m.Target().NativeBridge == android.NativeBridgeEnabled {
+		return false
+	}
+
+	return true
+}
+
+func generatePrebuiltSnapshot(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths {
+	/*
+		Snapshot zipped artifacts directory structure for etc modules:
+		{SNAPSHOT_ARCH}/
+			arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
+				etc/
+					(prebuilt etc files)
+			arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
+				etc/
+					(prebuilt etc files)
+			NOTICE_FILES/
+				(notice files)
+	*/
+	var snapshotOutputs android.Paths
+	noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
+	installedNotices := make(map[string]bool)
+
+	ctx.VisitAllModules(func(module android.Module) {
+		m, ok := module.(*PrebuiltEtc)
+		if !ok {
+			return
+		}
+
+		if !isSnapshotAware(ctx, m, s.Image) {
+			return
+		}
+
+		targetArch := "arch-" + m.Target().Arch.ArchType.String()
+
+		snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "etc", m.BaseModuleName())
+		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, m.OutputFile(), snapshotLibOut, s.Fake))
+
+		prop := snapshotJsonFlags{}
+		propOut := snapshotLibOut + ".json"
+		prop.ModuleName = m.BaseModuleName()
+		if m.subdirProperties.Relative_install_path != nil {
+			prop.RelativeInstallPath = *m.subdirProperties.Relative_install_path
+		}
+
+		if m.properties.Filename != nil {
+			prop.Filename = *m.properties.Filename
+		}
+
+		j, err := json.Marshal(prop)
+		if err != nil {
+			ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
+			return
+		}
+		snapshotOutputs = append(snapshotOutputs, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
+
+		if len(m.EffectiveLicenseFiles()) > 0 {
+			noticeName := ctx.ModuleName(m) + ".txt"
+			noticeOut := filepath.Join(noticeDir, noticeName)
+			// skip already copied notice file
+			if !installedNotices[noticeOut] {
+				installedNotices[noticeOut] = true
+
+				noticeOutPath := android.PathForOutput(ctx, noticeOut)
+				ctx.Build(pctx, android.BuildParams{
+					Rule:        android.Cat,
+					Inputs:      m.EffectiveLicenseFiles(),
+					Output:      noticeOutPath,
+					Description: "combine notices for " + noticeOut,
+				})
+				snapshotOutputs = append(snapshotOutputs, noticeOutPath)
+			}
+		}
+
+	})
+
+	return snapshotOutputs
+}
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 354f6bb..0c5cfe4 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -15,11 +15,15 @@
 package etc
 
 import (
+	"fmt"
 	"os"
 	"path/filepath"
 	"testing"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
+	"android/soong/snapshot"
 )
 
 func TestMain(m *testing.M) {
@@ -36,6 +40,18 @@
 	}),
 )
 
+var prepareForPrebuiltEtcSnapshotTest = android.GroupFixturePreparers(
+	prepareForPrebuiltEtcTest,
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		snapshot.VendorSnapshotImageSingleton.Init(ctx)
+		snapshot.RecoverySnapshotImageSingleton.Init(ctx)
+	}),
+	android.FixtureModifyConfig(func(config android.Config) {
+		config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
+		config.TestProductVariables.RecoverySnapshotVersion = proptools.StringPtr("current")
+	}),
+)
+
 func TestPrebuiltEtcVariants(t *testing.T) {
 	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_etc {
@@ -346,3 +362,110 @@
 		})
 	}
 }
+
+func checkIfSnapshotTaken(t *testing.T, result *android.TestResult, image string, moduleName string) {
+	checkIfSnapshotExistAsExpected(t, result, image, moduleName, true)
+}
+
+func checkIfSnapshotNotTaken(t *testing.T, result *android.TestResult, image string, moduleName string) {
+	checkIfSnapshotExistAsExpected(t, result, image, moduleName, false)
+}
+
+func checkIfSnapshotExistAsExpected(t *testing.T, result *android.TestResult, image string, moduleName string, expectToExist bool) {
+	snapshotSingleton := result.SingletonForTests(image + "-snapshot")
+	archType := "arm64"
+	archVariant := "armv8-a"
+	archDir := fmt.Sprintf("arch-%s", archType)
+
+	snapshotDir := fmt.Sprintf("%s-snapshot", image)
+	snapshotVariantPath := filepath.Join(snapshotDir, archType)
+	outputDir := filepath.Join(snapshotVariantPath, archDir, "etc")
+	imageVariant := ""
+	if image == "recovery" {
+		imageVariant = "recovery_"
+	}
+	mod := result.ModuleForTests(moduleName, fmt.Sprintf("android_%s%s_%s", imageVariant, archType, archVariant))
+	outputFiles := mod.OutputFiles(t, "")
+	if len(outputFiles) != 1 {
+		t.Errorf("%q must have single output\n", moduleName)
+		return
+	}
+	snapshotPath := filepath.Join(outputDir, moduleName)
+
+	if expectToExist {
+		out := snapshotSingleton.Output(snapshotPath)
+
+		if out.Input.String() != outputFiles[0].String() {
+			t.Errorf("The input of snapshot %q must be %q, but %q", "prebuilt_vendor", out.Input.String(), outputFiles[0])
+		}
+
+		snapshotJsonPath := snapshotPath + ".json"
+
+		if snapshotSingleton.MaybeOutput(snapshotJsonPath).Rule == nil {
+			t.Errorf("%q expected but not found", snapshotJsonPath)
+		}
+	} else {
+		out := snapshotSingleton.MaybeOutput(snapshotPath)
+		if out.Rule != nil {
+			t.Errorf("There must be no rule for module %q output file %q", moduleName, outputFiles[0])
+		}
+	}
+}
+
+func TestPrebuiltTakeSnapshot(t *testing.T) {
+	var testBp = `
+	prebuilt_etc {
+		name: "prebuilt_vendor",
+		src: "foo.conf",
+		vendor: true,
+	}
+
+	prebuilt_etc {
+		name: "prebuilt_vendor_indirect",
+		src: "foo.conf",
+		vendor: true,
+	}
+
+	prebuilt_etc {
+		name: "prebuilt_recovery",
+		src: "bar.conf",
+		recovery: true,
+	}
+
+	prebuilt_etc {
+		name: "prebuilt_recovery_indirect",
+		src: "bar.conf",
+		recovery: true,
+	}
+	`
+
+	t.Run("prebuilt: vendor and recovery snapshot", func(t *testing.T) {
+		result := prepareForPrebuiltEtcSnapshotTest.RunTestWithBp(t, testBp)
+
+		checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor")
+		checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor_indirect")
+		checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery")
+		checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery_indirect")
+	})
+
+	t.Run("prebuilt: directed snapshot", func(t *testing.T) {
+		prepareForPrebuiltEtcDirectedSnapshotTest := android.GroupFixturePreparers(
+			prepareForPrebuiltEtcSnapshotTest,
+			android.FixtureModifyConfig(func(config android.Config) {
+				config.TestProductVariables.DirectedVendorSnapshot = true
+				config.TestProductVariables.VendorSnapshotModules = make(map[string]bool)
+				config.TestProductVariables.VendorSnapshotModules["prebuilt_vendor"] = true
+				config.TestProductVariables.DirectedRecoverySnapshot = true
+				config.TestProductVariables.RecoverySnapshotModules = make(map[string]bool)
+				config.TestProductVariables.RecoverySnapshotModules["prebuilt_recovery"] = true
+			}),
+		)
+
+		result := prepareForPrebuiltEtcDirectedSnapshotTest.RunTestWithBp(t, testBp)
+
+		checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor")
+		checkIfSnapshotNotTaken(t, result, "vendor", "prebuilt_vendor_indirect")
+		checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery")
+		checkIfSnapshotNotTaken(t, result, "recovery", "prebuilt_recovery_indirect")
+	})
+}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 503c31f..1ce9911 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -599,7 +599,7 @@
 
 	// The monolithic hidden API processing also needs access to all the output files produced by
 	// hidden API processing of this fragment.
-	hiddenAPIInfo.HiddenAPIFlagOutput = (*output).HiddenAPIFlagOutput
+	hiddenAPIInfo.HiddenAPIFlagOutput = output.HiddenAPIFlagOutput
 
 	//  Provide it for use by other modules.
 	ctx.SetProvider(HiddenAPIInfoProvider, hiddenAPIInfo)
@@ -896,10 +896,10 @@
 
 // produceHiddenAPIOutput returns a path to the prebuilt all-flags.csv or nil if none is specified.
 func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
-	pathForOptionalSrc := func(src *string) android.Path {
+	pathForSrc := func(property string, src *string) android.Path {
 		if src == nil {
-			// TODO(b/179354495): Fail if this is not provided once prebuilts have been updated.
-			return nil
+			ctx.PropertyErrorf(property, "is required but was not specified")
+			return android.PathForModuleSrc(ctx, "missing", property)
 		}
 		return android.PathForModuleSrc(ctx, *src)
 	}
@@ -910,11 +910,11 @@
 
 	output := HiddenAPIOutput{
 		HiddenAPIFlagOutput: HiddenAPIFlagOutput{
-			StubFlagsPath:       pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags),
-			AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags),
-			MetadataPath:        pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata),
-			IndexPath:           pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index),
-			AllFlagsPath:        pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags),
+			AnnotationFlagsPath: pathForSrc("hidden_api.annotation_flags", module.prebuiltProperties.Hidden_api.Annotation_flags),
+			MetadataPath:        pathForSrc("hidden_api.metadata", module.prebuiltProperties.Hidden_api.Metadata),
+			IndexPath:           pathForSrc("hidden_api.index", module.prebuiltProperties.Hidden_api.Index),
+			StubFlagsPath:       pathForSrc("hidden_api.stub_flags", module.prebuiltProperties.Hidden_api.Stub_flags),
+			AllFlagsPath:        pathForSrc("hidden_api.all_flags", module.prebuiltProperties.Hidden_api.All_flags),
 		},
 		EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
 	}
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 654ebb7..86ab825 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -510,14 +510,6 @@
 	}
 }
 
-// dedup removes duplicates in the flag files, while maintaining the order in which they were
-// appended.
-func (s FlagFilesByCategory) dedup() {
-	for category, paths := range s {
-		s[category] = android.FirstUniquePaths(paths)
-	}
-}
-
 // HiddenAPIInfo contains information provided by the hidden API processing.
 //
 // That includes paths resolved from HiddenAPIFlagFileProperties and also generated by hidden API
diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go
index 52f0770..404b4c1 100644
--- a/java/hiddenapi_monolithic.go
+++ b/java/hiddenapi_monolithic.go
@@ -58,68 +58,33 @@
 	// Merge all the information from the classpathElements. The fragments form a DAG so it is possible that
 	// this will introduce duplicates so they will be resolved after processing all the classpathElements.
 	for _, element := range classpathElements {
-		var classesJars android.Paths
 		switch e := element.(type) {
 		case *ClasspathLibraryElement:
-			classesJars = retrieveClassesJarsFromModule(e.Module())
+			classesJars := retrieveClassesJarsFromModule(e.Module())
+			monolithicInfo.ClassesJars = append(monolithicInfo.ClassesJars, classesJars...)
 
 		case *ClasspathFragmentElement:
 			fragment := e.Module()
 			if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
 				info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
 				monolithicInfo.append(&info)
-
-				// If the bootclasspath fragment actually perform hidden API processing itself then use the
-				// CSV files it provides and do not bother processing the classesJars files. This ensures
-				// consistent behavior between source and prebuilt as prebuilt modules do not provide
-				// classesJars.
-				if info.AllFlagsPath != nil {
-					continue
-				}
+			} else {
+				ctx.ModuleErrorf("%s does not provide hidden API information", fragment)
 			}
-
-			classesJars = extractClassesJarsFromModules(e.Contents)
 		}
-
-		monolithicInfo.ClassesJars = append(monolithicInfo.ClassesJars, classesJars...)
 	}
 
-	// Dedup paths.
-	monolithicInfo.dedup()
-
 	return monolithicInfo
 }
 
 // append appends all the files from the supplied info to the corresponding files in this struct.
 func (i *MonolithicHiddenAPIInfo) append(other *HiddenAPIInfo) {
 	i.FlagsFilesByCategory.append(other.FlagFilesByCategory)
-
-	// The output may not be set if the bootclasspath_fragment has not yet been updated to support
-	// hidden API processing.
-	// TODO(b/179354495): Switch back to append once all bootclasspath_fragment modules have been
-	//  updated to support hidden API processing properly.
-	appendIfNotNil := func(paths android.Paths, path android.Path) android.Paths {
-		if path == nil {
-			return paths
-		}
-		return append(paths, path)
-	}
-	i.StubFlagsPaths = appendIfNotNil(i.StubFlagsPaths, other.StubFlagsPath)
-	i.AnnotationFlagsPaths = appendIfNotNil(i.AnnotationFlagsPaths, other.AnnotationFlagsPath)
-	i.MetadataPaths = appendIfNotNil(i.MetadataPaths, other.MetadataPath)
-	i.IndexPaths = appendIfNotNil(i.IndexPaths, other.IndexPath)
-	i.AllFlagsPaths = appendIfNotNil(i.AllFlagsPaths, other.AllFlagsPath)
-}
-
-// dedup removes duplicates in all the paths, while maintaining the order in which they were
-// appended.
-func (i *MonolithicHiddenAPIInfo) dedup() {
-	i.FlagsFilesByCategory.dedup()
-	i.StubFlagsPaths = android.FirstUniquePaths(i.StubFlagsPaths)
-	i.AnnotationFlagsPaths = android.FirstUniquePaths(i.AnnotationFlagsPaths)
-	i.MetadataPaths = android.FirstUniquePaths(i.MetadataPaths)
-	i.IndexPaths = android.FirstUniquePaths(i.IndexPaths)
-	i.AllFlagsPaths = android.FirstUniquePaths(i.AllFlagsPaths)
+	i.StubFlagsPaths = append(i.StubFlagsPaths, other.StubFlagsPath)
+	i.AnnotationFlagsPaths = append(i.AnnotationFlagsPaths, other.AnnotationFlagsPath)
+	i.MetadataPaths = append(i.MetadataPaths, other.MetadataPath)
+	i.IndexPaths = append(i.IndexPaths, other.IndexPath)
+	i.AllFlagsPaths = append(i.AllFlagsPaths, other.AllFlagsPath)
 }
 
 var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{})
diff --git a/rust/Android.bp b/rust/Android.bp
index 11069d1..221014e 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -11,6 +11,7 @@
         "soong-bloaty",
         "soong-cc",
         "soong-rust-config",
+        "soong-snapshot",
     ],
     srcs: [
         "androidmk.go",
diff --git a/rust/library.go b/rust/library.go
index 5a36bd1..8c10e29 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -21,6 +21,7 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	"android/soong/snapshot"
 )
 
 var (
@@ -645,7 +646,7 @@
 			variation := v.(*Module).ModuleBase.ImageVariation().Variation
 			if strings.HasPrefix(variation, cc.VendorVariationPrefix) &&
 				m.HasVendorVariant() &&
-				!cc.IsVendorProprietaryModule(mctx) &&
+				!snapshot.IsVendorProprietaryModule(mctx) &&
 				strings.TrimPrefix(variation, cc.VendorVariationPrefix) == mctx.DeviceConfig().VndkVersion() {
 
 				// cc.MutateImage runs before LibraryMutator, so vendor variations which are meant for rlibs only are
diff --git a/rust/snapshot_prebuilt.go b/rust/snapshot_prebuilt.go
index 2f54973..79eaab3 100644
--- a/rust/snapshot_prebuilt.go
+++ b/rust/snapshot_prebuilt.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"android/soong/cc"
+
 	"github.com/google/blueprint/proptools"
 )
 
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
index 7472f52..c50dc24 100644
--- a/scripts/hiddenapi/Android.bp
+++ b/scripts/hiddenapi/Android.bp
@@ -48,6 +48,27 @@
     },
 }
 
+python_test_host {
+    name: "generate_hiddenapi_lists_test",
+    main: "generate_hiddenapi_lists_test.py",
+    srcs: [
+        "generate_hiddenapi_lists.py",
+        "generate_hiddenapi_lists_test.py",
+    ],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+    },
+    test_options: {
+        unit_test: true,
+    },
+}
+
 python_binary_host {
     name: "verify_overlaps",
     main: "verify_overlaps.py",
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists_test.py b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
index ff3d708..b81424b 100755
--- a/scripts/hiddenapi/generate_hiddenapi_lists_test.py
+++ b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
@@ -101,4 +101,4 @@
         self.assertEqual(extract_package(signature), expected_package)
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main(verbosity=2)
diff --git a/snapshot/Android.bp b/snapshot/Android.bp
new file mode 100644
index 0000000..f17ac53
--- /dev/null
+++ b/snapshot/Android.bp
@@ -0,0 +1,22 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-snapshot",
+    pkgPath: "android/soong/snapshot",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "soong",
+        "soong-android",
+    ],
+    srcs: [
+        "recovery_snapshot.go",
+        "snapshot.go",
+        "snapshot_base.go",
+        "util.go",
+        "vendor_snapshot.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/snapshot/recovery_snapshot.go b/snapshot/recovery_snapshot.go
new file mode 100644
index 0000000..9b3919c
--- /dev/null
+++ b/snapshot/recovery_snapshot.go
@@ -0,0 +1,130 @@
+// Copyright 2021 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 snapshot
+
+import "android/soong/android"
+
+// Interface for modules which can be captured in the recovery snapshot.
+type RecoverySnapshotModuleInterface interface {
+	SnapshotModuleInterfaceBase
+	InRecovery() bool
+	ExcludeFromRecoverySnapshot() bool
+}
+
+var recoverySnapshotSingleton = SnapshotSingleton{
+	"recovery",                     // name
+	"SOONG_RECOVERY_SNAPSHOT_ZIP",  // makeVar
+	android.OptionalPath{},         // snapshotZipFile
+	RecoverySnapshotImageSingleton, // Image
+	false,                          // Fake
+}
+
+func RecoverySnapshotSingleton() android.Singleton {
+	return &recoverySnapshotSingleton
+}
+
+// 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)
+}
+
+func IsRecoveryProprietaryModule(ctx android.BaseModuleContext) bool {
+
+	// Any module in a recovery proprietary path is a recovery proprietary
+	// module.
+	if isRecoveryProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
+		return true
+	}
+
+	// However if the module is not in a recovery proprietary path, it may
+	// still be a recovery proprietary module. This happens for cc modules
+	// that are excluded from the recovery snapshot, and it means that the
+	// vendor has assumed control of the framework-provided module.
+
+	if c, ok := ctx.Module().(RecoverySnapshotModuleInterface); ok {
+		if c.ExcludeFromRecoverySnapshot() {
+			return true
+		}
+	}
+
+	return false
+}
+
+var RecoverySnapshotImageName = "recovery"
+
+type RecoverySnapshotImage struct{}
+
+func (RecoverySnapshotImage) Init(ctx android.RegistrationContext) {
+	ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
+}
+
+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 SnapshotModuleInterfaceBase) func() bool {
+	r, ok := m.(RecoverySnapshotModuleInterface)
+
+	if !ok {
+		// This module does not support recovery snapshot
+		return func() bool { return false }
+	}
+	return r.InRecovery
+}
+
+func (RecoverySnapshotImage) IsProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return isDirectoryExcluded(dir, deviceConfig.RecoverySnapshotDirsExcludedMap(), deviceConfig.RecoverySnapshotDirsIncludedMap())
+}
+
+func (RecoverySnapshotImage) ExcludeFromSnapshot(m SnapshotModuleInterfaceBase) bool {
+	r, ok := m.(RecoverySnapshotModuleInterface)
+
+	if !ok {
+		// This module does not support recovery snapshot
+		return true
+	}
+	return r.ExcludeFromRecoverySnapshot()
+}
+
+func (RecoverySnapshotImage) IsUsingSnapshot(cfg android.DeviceConfig) bool {
+	recoverySnapshotVersion := cfg.RecoverySnapshotVersion()
+	return recoverySnapshotVersion != "current" && recoverySnapshotVersion != ""
+}
+
+func (RecoverySnapshotImage) TargetSnapshotVersion(cfg android.DeviceConfig) string {
+	return cfg.RecoverySnapshotVersion()
+}
+
+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 (RecoverySnapshotImage) ImageName() string {
+	return RecoverySnapshotImageName
+}
+
+var RecoverySnapshotImageSingleton RecoverySnapshotImage
+
+func init() {
+	RecoverySnapshotImageSingleton.Init(android.InitRegistrationContext)
+}
diff --git a/snapshot/snapshot.go b/snapshot/snapshot.go
new file mode 100644
index 0000000..294f8b6
--- /dev/null
+++ b/snapshot/snapshot.go
@@ -0,0 +1,122 @@
+// Copyright 2021 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 snapshot
+
+import (
+	"path/filepath"
+	"sort"
+
+	"android/soong/android"
+)
+
+// This file contains singletons to capture snapshots. This singleton will generate snapshot of each target
+// image, and capturing snapshot module will be delegated to each module which implements GenerateSnapshotAction
+// function and register with RegisterSnapshotAction.
+
+var pctx = android.NewPackageContext("android/soong/snapshot")
+
+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
+
+	// 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
+}
+
+// Interface of function to capture snapshot from each module
+type GenerateSnapshotAction func(snapshot SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths
+
+var snapshotActionList []GenerateSnapshotAction
+
+// Register GenerateSnapshotAction function so it can be called while generating snapshot
+func RegisterSnapshotAction(x GenerateSnapshotAction) {
+	snapshotActionList = append(snapshotActionList, x)
+}
+
+func (c *SnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	if !c.Image.shouldGenerateSnapshot(ctx) {
+		return
+	}
+
+	var snapshotOutputs android.Paths
+
+	// Snapshot zipped artifacts will be captured under {SNAPSHOT_ARCH} directory
+
+	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())
+
+	for _, f := range snapshotActionList {
+		snapshotOutputs = append(snapshotOutputs, f(*c, ctx, snapshotArchDir)...)
+	}
+
+	// All artifacts are ready. Sort them to normalize ninja and then zip.
+	sort.Slice(snapshotOutputs, func(i, j int) bool {
+		return snapshotOutputs[i].String() < snapshotOutputs[j].String()
+	})
+
+	zipPath := android.PathForOutput(
+		ctx,
+		snapshotDir,
+		c.name+"-"+ctx.Config().DeviceName()+".zip")
+	zipRule := android.NewRuleBuilder(pctx, ctx)
+
+	// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
+	snapshotOutputList := android.PathForOutput(
+		ctx,
+		snapshotDir,
+		c.name+"-"+ctx.Config().DeviceName()+"_list")
+	rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp")
+	zipRule.Command().
+		Text("tr").
+		FlagWithArg("-d ", "\\'").
+		FlagWithRspFileInputList("< ", rspFile, snapshotOutputs).
+		FlagWithOutput("> ", snapshotOutputList)
+
+	zipRule.Temporary(snapshotOutputList)
+
+	zipRule.Command().
+		BuiltTool("soong_zip").
+		FlagWithOutput("-o ", zipPath).
+		FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
+		FlagWithInput("-l ", snapshotOutputList)
+
+	zipRule.Build(zipPath.String(), c.name+" snapshot "+zipPath.String())
+	zipRule.DeleteTemporaryFiles()
+	c.snapshotZipFile = android.OptionalPathForPath(zipPath)
+}
+
+func (c *SnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.Strict(
+		c.makeVar,
+		c.snapshotZipFile.String())
+}
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
new file mode 100644
index 0000000..de93f3e
--- /dev/null
+++ b/snapshot/snapshot_base.go
@@ -0,0 +1,104 @@
+// Copyright 2021 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 snapshot
+
+import (
+	"android/soong/android"
+	"path/filepath"
+)
+
+// Interface for modules which can be captured in the snapshot.
+type SnapshotModuleInterfaceBase interface{}
+
+// Defines the specifics of different images to which the snapshot process is applicable, e.g.,
+// vendor, recovery, ramdisk.
+type SnapshotImage interface {
+	// 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 SnapshotModuleInterfaceBase) func() 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 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 SnapshotModuleInterfaceBase) bool
+
+	// Returns true if the build is using a snapshot for this image.
+	IsUsingSnapshot(cfg android.DeviceConfig) bool
+
+	// Returns a version of which the snapshot should be used in this target.
+	// This will only be meaningful when isUsingSnapshot is true.
+	TargetSnapshotVersion(cfg android.DeviceConfig) string
+
+	// 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
+
+	// Returns target image name
+	ImageName() string
+}
+
+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,
+	}
+
+	// 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 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)
+	}
+}
diff --git a/snapshot/util.go b/snapshot/util.go
new file mode 100644
index 0000000..2297dfc
--- /dev/null
+++ b/snapshot/util.go
@@ -0,0 +1,36 @@
+// Copyright 2021 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 snapshot
+
+import "android/soong/android"
+
+func WriteStringToFileRule(ctx android.SingletonContext, content, out string) android.OutputPath {
+	outPath := android.PathForOutput(ctx, out)
+	android.WriteFileRule(ctx, outPath, content)
+	return outPath
+}
+
+func CopyFileRule(pctx android.PackageContext, 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: "copy " + path.String() + " -> " + out,
+		Args: map[string]string{
+			"cpFlags": "-f -L",
+		},
+	})
+	return outPath
+}
diff --git a/snapshot/vendor_snapshot.go b/snapshot/vendor_snapshot.go
new file mode 100644
index 0000000..9bd26c2
--- /dev/null
+++ b/snapshot/vendor_snapshot.go
@@ -0,0 +1,147 @@
+// Copyright 2021 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 snapshot
+
+import "android/soong/android"
+
+// Interface for modules which can be captured in the vendor snapshot.
+type VendorSnapshotModuleInterface interface {
+	SnapshotModuleInterfaceBase
+	InVendor() bool
+	ExcludeFromVendorSnapshot() bool
+}
+
+var vendorSnapshotSingleton = SnapshotSingleton{
+	"vendor",                     // name
+	"SOONG_VENDOR_SNAPSHOT_ZIP",  // makeVar
+	android.OptionalPath{},       // snapshotZipFile
+	VendorSnapshotImageSingleton, // Image
+	false,                        // Fake
+}
+
+var vendorFakeSnapshotSingleton = SnapshotSingleton{
+	"vendor",                         // name
+	"SOONG_VENDOR_FAKE_SNAPSHOT_ZIP", // makeVar
+	android.OptionalPath{},           // snapshotZipFile
+	VendorSnapshotImageSingleton,     // Image
+	true,                             // Fake
+}
+
+func VendorSnapshotSingleton() android.Singleton {
+	return &vendorSnapshotSingleton
+}
+
+func VendorFakeSnapshotSingleton() android.Singleton {
+	return &vendorFakeSnapshotSingleton
+}
+
+// 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)
+}
+
+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
+	}
+
+	// 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().(VendorSnapshotModuleInterface); ok {
+		if c.ExcludeFromVendorSnapshot() {
+			return true
+		}
+	}
+
+	return false
+}
+
+var VendorSnapshotImageName = "vendor"
+
+type VendorSnapshotImage struct{}
+
+func (VendorSnapshotImage) Init(ctx android.RegistrationContext) {
+	ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+	ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+}
+
+func (VendorSnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
+	ctx.RegisterModuleType(name, factory)
+}
+
+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 SnapshotModuleInterfaceBase) func() bool {
+	v, ok := m.(VendorSnapshotModuleInterface)
+
+	if !ok {
+		// This module does not support Vendor snapshot
+		return func() bool { return false }
+	}
+
+	return v.InVendor
+}
+
+func (VendorSnapshotImage) IsProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return isDirectoryExcluded(dir, deviceConfig.VendorSnapshotDirsExcludedMap(), deviceConfig.VendorSnapshotDirsIncludedMap())
+}
+
+func (VendorSnapshotImage) ExcludeFromSnapshot(m SnapshotModuleInterfaceBase) bool {
+	v, ok := m.(VendorSnapshotModuleInterface)
+
+	if !ok {
+		// This module does not support Vendor snapshot
+		return true
+	}
+
+	return v.ExcludeFromVendorSnapshot()
+}
+
+func (VendorSnapshotImage) IsUsingSnapshot(cfg android.DeviceConfig) bool {
+	vndkVersion := cfg.VndkVersion()
+	return vndkVersion != "current" && vndkVersion != ""
+}
+
+func (VendorSnapshotImage) TargetSnapshotVersion(cfg android.DeviceConfig) string {
+	return cfg.VndkVersion()
+}
+
+// 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 (VendorSnapshotImage) ImageName() string {
+	return VendorSnapshotImageName
+}
+
+var VendorSnapshotImageSingleton VendorSnapshotImage
+
+func init() {
+	VendorSnapshotImageSingleton.Init(android.InitRegistrationContext)
+}