Merge "Drop "prebuilt_" prefix when adding libraries to class loader context."
diff --git a/android/bazel.go b/android/bazel.go
index 992d8aa..8d13762 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -180,7 +180,6 @@
 		//                                                       also depends on //system/logging/liblog:liblog (http://b/186822772)
 		"libc_ndk",          // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661)
 		"libc_malloc_hooks", // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook
-		"libm",              // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list
 
 		// http://b/186823769: Needs C++ STL support, includes from unconverted standard libraries in //external/libcxx
 		// c++_static
@@ -189,7 +188,7 @@
 		"libBionicBenchmarksUtils", // cc_library_static, fatal error: 'map' file not found, from libcxx
 		"fmtlib",                   // cc_library_static, fatal error: 'cassert' file not found, from libcxx
 		"fmtlib_ndk",               // cc_library_static, fatal error: 'cassert' file not found
-		"libbase",                  // http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx
+		"libbase",                  // Requires liblog. http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx.
 
 		// http://b/186024507: Includes errors because of the system_shared_libs default value.
 		// Missing -isystem bionic/libc/include through the libc/libm/libdl
diff --git a/android/config.go b/android/config.go
index ed90c31..396b1a6 100644
--- a/android/config.go
+++ b/android/config.go
@@ -821,6 +821,12 @@
 	return Bool(c.productVariables.Unbundled_build_apps)
 }
 
+// Returns true if building image that aren't bundled with the platform.
+// UnbundledBuild() is always true when this is true.
+func (c *config) UnbundledBuildImage() bool {
+	return Bool(c.productVariables.Unbundled_build_image)
+}
+
 // Returns true if building modules against prebuilt SDKs.
 func (c *config) AlwaysUsePrebuiltSdks() bool {
 	return Bool(c.productVariables.Always_use_prebuilt_sdks)
diff --git a/android/module.go b/android/module.go
index 07d82f1..126d629 100644
--- a/android/module.go
+++ b/android/module.go
@@ -344,8 +344,18 @@
 	// Deprecated: use ModuleContext.Build instead.
 	ModuleBuild(pctx PackageContext, params ModuleBuildParams)
 
+	// Returns a list of paths expanded from globs and modules referenced using ":module" syntax.  The property must
+	// be tagged with `android:"path" to support automatic source module dependency resolution.
+	//
+	// Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead.
 	ExpandSources(srcFiles, excludes []string) Paths
+
+	// Returns a single path expanded from globs and modules referenced using ":module" syntax.  The property must
+	// be tagged with `android:"path" to support automatic source module dependency resolution.
+	//
+	// Deprecated: use PathForModuleSrc instead.
 	ExpandSource(srcFile, prop string) Path
+
 	ExpandOptionalSource(srcFile *string, prop string) OptionalPath
 
 	// InstallExecutable creates a rule to copy srcPath to name in the installPath directory,
@@ -473,6 +483,7 @@
 	InitRc() Paths
 	VintfFragments() Paths
 	NoticeFiles() Paths
+	EffectiveLicenseFiles() Paths
 
 	AddProperties(props ...interface{})
 	GetProperties() []interface{}
@@ -1505,6 +1516,10 @@
 	return m.commonProperties.NamespaceExportedToMake
 }
 
+func (m *ModuleBase) EffectiveLicenseFiles() Paths {
+	return m.commonProperties.Effective_license_text
+}
+
 // computeInstallDeps finds the installed paths of all dependencies that have a dependency
 // tag that is annotated as needing installation via the IsInstallDepNeeded method.
 func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSet, []*packagingSpecsDepSet) {
@@ -2797,30 +2812,57 @@
 	return m.bp
 }
 
-// SrcIsModule decodes module references in the format ":name" into the module name, or empty string if the input
-// was not a module reference.
+// SrcIsModule decodes module references in the format ":unqualified-name" or "//namespace:name"
+// into the module name, or empty string if the input was not a module reference.
 func SrcIsModule(s string) (module string) {
-	if len(s) > 1 && s[0] == ':' {
-		return s[1:]
+	if len(s) > 1 {
+		if s[0] == ':' {
+			module = s[1:]
+			if !isUnqualifiedModuleName(module) {
+				// The module name should be unqualified but is not so do not treat it as a module.
+				module = ""
+			}
+		} else if s[0] == '/' && s[1] == '/' {
+			module = s
+		}
 	}
-	return ""
+	return module
 }
 
-// SrcIsModule decodes module references in the format ":name{.tag}" into the module name and tag, ":name" into the
-// module name and an empty string for the tag, or empty strings if the input was not a module reference.
+// SrcIsModule decodes module references in the format ":unqualified-name{.tag}" or
+// "//namespace:name{.tag}" into the module name and an empty string for the tag, or empty strings
+// if the input was not a module reference.
 func SrcIsModuleWithTag(s string) (module, tag string) {
-	if len(s) > 1 && s[0] == ':' {
-		module = s[1:]
-		if tagStart := strings.IndexByte(module, '{'); tagStart > 0 {
-			if module[len(module)-1] == '}' {
-				tag = module[tagStart+1 : len(module)-1]
-				module = module[:tagStart]
-				return module, tag
+	if len(s) > 1 {
+		if s[0] == ':' {
+			module = s[1:]
+		} else if s[0] == '/' && s[1] == '/' {
+			module = s
+		}
+
+		if module != "" {
+			if tagStart := strings.IndexByte(module, '{'); tagStart > 0 {
+				if module[len(module)-1] == '}' {
+					tag = module[tagStart+1 : len(module)-1]
+					module = module[:tagStart]
+				}
+			}
+
+			if s[0] == ':' && !isUnqualifiedModuleName(module) {
+				// The module name should be unqualified but is not so do not treat it as a module.
+				module = ""
+				tag = ""
 			}
 		}
-		return module, ""
 	}
-	return "", ""
+
+	return module, tag
+}
+
+// isUnqualifiedModuleName makes sure that the supplied module is an unqualified module name, i.e.
+// does not contain any /.
+func isUnqualifiedModuleName(module string) bool {
+	return strings.IndexByte(module, '/') == -1
 }
 
 type sourceOrOutputDependencyTag struct {
@@ -2832,8 +2874,26 @@
 	return sourceOrOutputDependencyTag{tag: tag}
 }
 
+// Deprecated, use IsSourceDepTagWithOutputTag(tag, "") instead.
 var SourceDepTag = sourceOrOutputDepTag("")
 
+// IsSourceDepTag returns true if the supplied blueprint.DependencyTag is one that was used to add
+// dependencies by either ExtractSourceDeps, ExtractSourcesDeps or automatically for properties
+// tagged with `android:"path"`.
+func IsSourceDepTag(depTag blueprint.DependencyTag) bool {
+	_, ok := depTag.(sourceOrOutputDependencyTag)
+	return ok
+}
+
+// IsSourceDepTagWithOutputTag returns true if the supplied blueprint.DependencyTag is one that was
+// used to add dependencies by either ExtractSourceDeps, ExtractSourcesDeps or automatically for
+// properties tagged with `android:"path"` AND it was added using a module reference of
+// :moduleName{outputTag}.
+func IsSourceDepTagWithOutputTag(depTag blueprint.DependencyTag, outputTag string) bool {
+	t, ok := depTag.(sourceOrOutputDependencyTag)
+	return ok && t.tag == outputTag
+}
+
 // Adds necessary dependencies to satisfy filegroup or generated sources modules listed in srcFiles
 // using ":module" syntax, if any.
 //
diff --git a/android/module_test.go b/android/module_test.go
index 9ac9291..9e2b0ca 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -55,6 +55,27 @@
 			},
 			wantModule: "foo:bar",
 		},
+		{
+			name: "fully qualified",
+			args: args{
+				s: "//foo:bar",
+			},
+			wantModule: "//foo:bar",
+		},
+		{
+			name: "fully qualified with tag",
+			args: args{
+				s: "//foo:bar{.tag}",
+			},
+			wantModule: "//foo:bar{.tag}",
+		},
+		{
+			name: "invalid unqualified name",
+			args: args{
+				s: ":foo/bar",
+			},
+			wantModule: "",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -128,6 +149,35 @@
 			},
 			wantModule: "foo.bar}",
 		},
+		{
+			name: "fully qualified",
+			args: args{
+				s: "//foo:bar",
+			},
+			wantModule: "//foo:bar",
+		},
+		{
+			name: "fully qualified with tag",
+			args: args{
+				s: "//foo:bar{.tag}",
+			},
+			wantModule: "//foo:bar",
+			wantTag:    ".tag",
+		},
+		{
+			name: "invalid unqualified name",
+			args: args{
+				s: ":foo/bar",
+			},
+			wantModule: "",
+		},
+		{
+			name: "invalid unqualified name with tag",
+			args: args{
+				s: ":foo/bar{.tag}",
+			},
+			wantModule: "",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/android/mutator.go b/android/mutator.go
index 365bf29..819dd0f 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -19,6 +19,7 @@
 	"fmt"
 	"reflect"
 	"strings"
+	"sync"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -229,6 +230,9 @@
 var bp2buildDepsMutators = []RegisterMutatorFunc{}
 var bp2buildMutators = map[string]RegisterMutatorFunc{}
 
+// See http://b/192523357
+var bp2buildLock sync.Mutex
+
 // RegisterBp2BuildMutator registers specially crafted mutators for
 // converting Blueprint/Android modules into special modules that can
 // be code-generated into Bazel BUILD targets.
@@ -238,6 +242,9 @@
 	f := func(ctx RegisterMutatorsContext) {
 		ctx.TopDown(moduleType, m)
 	}
+	// Use a lock to avoid a concurrent map write if RegisterBp2BuildMutator is called in parallel
+	bp2buildLock.Lock()
+	defer bp2buildLock.Unlock()
 	bp2buildMutators[moduleType] = f
 }
 
diff --git a/android/namespace.go b/android/namespace.go
index d137636..4f727e1 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -27,7 +27,11 @@
 )
 
 func init() {
-	RegisterModuleType("soong_namespace", NamespaceFactory)
+	registerNamespaceBuildComponents(InitRegistrationContext)
+}
+
+func registerNamespaceBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
 }
 
 // threadsafe sorted list
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 08e221a..ea399da 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -636,13 +636,12 @@
 	result := GroupFixturePreparers(
 		FixtureModifyContext(func(ctx *TestContext) {
 			ctx.RegisterModuleType("test_module", newTestModule)
-			ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
 			ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
-			ctx.PreArchMutators(RegisterNamespaceMutator)
 			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
 				ctx.BottomUp("rename", renameMutator)
 			})
 		}),
+		PrepareForTestWithNamespace,
 		bps.AddToFixture(),
 	).
 		// Ignore errors for now so tests can check them later.
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index 568f868..07b4869 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -63,7 +63,8 @@
 
 	if p.props.Foo != "" {
 		// Make sure there is only one dependency on a module listed in a property present in multiple property structs
-		if ctx.GetDirectDepWithTag(SrcIsModule(p.props.Foo), sourceOrOutputDepTag("")) == nil {
+		m := SrcIsModule(p.props.Foo)
+		if GetModuleFromPathDep(ctx, m, "") == nil {
 			ctx.ModuleErrorf("GetDirectDepWithTag failed")
 		}
 	}
diff --git a/android/paths.go b/android/paths.go
index 128ec12..99d5ba7 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -446,7 +446,7 @@
 // If the dependency is not found, a missingErrorDependency is returned.
 // If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned.
 func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag string) (Paths, error) {
-	module := ctx.GetDirectDepWithTag(moduleName, sourceOrOutputDepTag(tag))
+	module := GetModuleFromPathDep(ctx, moduleName, tag)
 	if module == nil {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
@@ -474,6 +474,22 @@
 	}
 }
 
+// GetModuleFromPathDep will return the module that was added as a dependency automatically for
+// properties tagged with `android:"path"` or manually using ExtractSourceDeps or
+// ExtractSourcesDeps.
+//
+// The moduleName and tag supplied to this should be the values returned from SrcIsModuleWithTag.
+// Or, if no tag is expected then the moduleName should be the value returned by  SrcIsModule and
+// the tag must be "".
+//
+// If tag is "" then the returned module will be the dependency that was added for ":moduleName".
+// Otherwise, it is the dependency that was added for ":moduleName{tag}".
+//
+// TODO(b/193228441) Make this handle fully qualified names, e.g. //namespace:moduleName.
+func GetModuleFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) blueprint.Module {
+	return ctx.GetDirectDepWithTag(moduleName, sourceOrOutputDepTag(tag))
+}
+
 // PathsAndMissingDepsForModuleSrcExcludes returns a Paths{} containing the resolved references in
 // paths, minus those listed in excludes. Elements of paths and excludes are resolved as:
 // * filepath, relative to local module directory, resolves as a filepath relative to the local
@@ -1252,10 +1268,11 @@
 // PathForModuleSrc returns a Path representing the paths... under the
 // module's local source directory.
 func PathForModuleSrc(ctx ModuleMissingDepsPathContext, pathComponents ...string) Path {
-	p, err := validatePath(pathComponents...)
-	if err != nil {
-		reportPathError(ctx, err)
-	}
+	// Just join the components textually just to make sure that it does not corrupt a fully qualified
+	// module reference, e.g. if the pathComponents is "://other:foo" then using filepath.Join() or
+	// validatePath() will corrupt it, e.g. replace "//" with "/". If the path is not a module
+	// reference then it will be validated by expandOneSrcPath anyway when it calls expandOneSrcPath.
+	p := strings.Join(pathComponents, string(filepath.Separator))
 	paths, err := expandOneSrcPath(ctx, p, nil)
 	if err != nil {
 		if depErr, ok := err.(missingDependencyError); ok {
diff --git a/android/paths_test.go b/android/paths_test.go
index 6f5d79e..7675905 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -1125,6 +1125,12 @@
 	rels []string
 	src  string
 	rel  string
+
+	// Make test specific preparations to the test fixture.
+	preparer FixturePreparer
+
+	// A test specific error handler.
+	errorHandler FixtureErrorHandler
 }
 
 func testPathForModuleSrc(t *testing.T, tests []pathForModuleSrcTestCase) {
@@ -1157,14 +1163,23 @@
 				"foo/src_special/$": nil,
 			}
 
+			errorHandler := test.errorHandler
+			if errorHandler == nil {
+				errorHandler = FixtureExpectsNoErrors
+			}
+
 			result := GroupFixturePreparers(
 				FixtureRegisterWithContext(func(ctx RegistrationContext) {
 					ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
 					ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory)
-					ctx.RegisterModuleType("filegroup", FileGroupFactory)
 				}),
+				PrepareForTestWithFilegroup,
+				PrepareForTestWithNamespace,
 				mockFS.AddToFixture(),
-			).RunTest(t)
+				OptionalFixturePreparer(test.preparer),
+			).
+				ExtendWithErrorHandler(errorHandler).
+				RunTest(t)
 
 			m := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
 
@@ -1333,6 +1348,74 @@
 			src: "foo/src_special/$",
 			rel: "src_special/$",
 		},
+		{
+			// This test makes sure that an unqualified module name cannot contain characters that make
+			// it appear as a qualified module name.
+			name: "output file provider, invalid fully qualified name",
+			bp: `
+			test {
+				name: "foo",
+				src: "://other:b",
+				srcs: ["://other:c"],
+			}`,
+			preparer: FixtureAddTextFile("other/Android.bp", `
+				soong_namespace {}
+
+				output_file_provider {
+					name: "b",
+					outs: ["gen/b"],
+				}
+
+				output_file_provider {
+					name: "c",
+					outs: ["gen/c"],
+				}
+			`),
+			src:  "foo/:/other:b",
+			rel:  ":/other:b",
+			srcs: []string{"foo/:/other:c"},
+			rels: []string{":/other:c"},
+		},
+		{
+			name: "output file provider, missing fully qualified name",
+			bp: `
+			test {
+				name: "foo",
+				src: "//other:b",
+				srcs: ["//other:c"],
+			}`,
+			errorHandler: FixtureExpectsAllErrorsToMatchAPattern([]string{
+				`"foo" depends on undefined module "//other:b"`,
+				`"foo" depends on undefined module "//other:c"`,
+			}),
+		},
+		{
+			// TODO(b/193228441): Fix broken test.
+			name: "output file provider, fully qualified name",
+			bp: `
+			test {
+				name: "foo",
+				src: "//other:b",
+				srcs: ["//other:c"],
+			}`,
+			preparer: FixtureAddTextFile("other/Android.bp", `
+				soong_namespace {}
+
+				output_file_provider {
+					name: "b",
+					outs: ["gen/b"],
+				}
+
+				output_file_provider {
+					name: "c",
+					outs: ["gen/c"],
+				}
+			`),
+			errorHandler: FixtureExpectsAllErrorsToMatchAPattern([]string{
+				`"foo": missing dependencies: //other:b, is the property annotated with android:"path"`,
+				`"foo": missing dependency on "//other:c", is the property annotated with android:"path"`,
+			}),
+		},
 	}
 
 	testPathForModuleSrc(t, tests)
diff --git a/android/prebuilt.go b/android/prebuilt.go
index f3493bd..e611502 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -61,6 +61,16 @@
 	// a matching name.
 	Prefer *bool `android:"arch_variant"`
 
+	// When specified this names a Soong config variable that controls the prefer property.
+	//
+	// If the value of the named Soong config variable is true then prefer is set to false and vice
+	// versa. If the Soong config variable is not set then it defaults to false, so prefer defaults
+	// to true.
+	//
+	// If specified then the prefer property is ignored in favor of the value of the Soong config
+	// variable.
+	Use_source_config_var *ConfigVarProperties
+
 	SourceExists bool `blueprint:"mutated"`
 	UsePrebuilt  bool `blueprint:"mutated"`
 
@@ -68,6 +78,22 @@
 	PrebuiltRenamedToSource bool `blueprint:"mutated"`
 }
 
+// Properties that can be used to select a Soong config variable.
+type ConfigVarProperties struct {
+	// Allow instances of this struct to be used as a property value in a BpPropertySet.
+	BpPrintableBase
+
+	// The name of the configuration namespace.
+	//
+	// As passed to add_soong_config_namespace in Make.
+	Config_namespace *string
+
+	// The name of the configuration variable.
+	//
+	// As passed to add_soong_config_var_value in Make.
+	Var_name *string
+}
+
 type Prebuilt struct {
 	properties PrebuiltProperties
 
@@ -364,12 +390,18 @@
 		return false
 	}
 
-	// TODO: use p.Properties.Name and ctx.ModuleDir to override preference
-	if Bool(p.properties.Prefer) {
+	// If source is not available or is disabled then always use the prebuilt.
+	if source == nil || !source.Enabled() {
 		return true
 	}
 
-	return source == nil || !source.Enabled()
+	// If the use_source_config_var property is set then it overrides the prefer property setting.
+	if configVar := p.properties.Use_source_config_var; configVar != nil {
+		return !ctx.Config().VendorConfig(proptools.String(configVar.Config_namespace)).Bool(proptools.String(configVar.Var_name))
+	}
+
+	// TODO: use p.Properties.Name and ctx.ModuleDir to override preference
+	return Bool(p.properties.Prefer)
 }
 
 func (p *Prebuilt) SourceExists() bool {
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 23524a5..dcd77ea 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -26,6 +26,7 @@
 	replaceBp bool // modules is added to default bp boilerplate if false.
 	modules   string
 	prebuilt  []OsType
+	preparer  FixturePreparer
 }{
 	{
 		name: "no prebuilt",
@@ -291,6 +292,86 @@
 			}`,
 		prebuilt: []OsType{Android, BuildOs},
 	},
+	{
+		name: "prebuilt use_source_config_var={acme, use_source} - no var specified",
+		modules: `
+			source {
+				name: "bar",
+			}
+
+			prebuilt {
+				name: "bar",
+				use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
+				srcs: ["prebuilt_file"],
+			}`,
+		// When use_source_env is specified then it will use the prebuilt by default if the environment
+		// variable is not set.
+		prebuilt: []OsType{Android, BuildOs},
+	},
+	{
+		name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=false",
+		modules: `
+			source {
+				name: "bar",
+			}
+
+			prebuilt {
+				name: "bar",
+				use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
+				srcs: ["prebuilt_file"],
+			}`,
+		preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.VendorVars = map[string]map[string]string{
+				"acme": {
+					"use_source": "false",
+				},
+			}
+		}),
+		// Setting the environment variable named in use_source_env to false will cause the prebuilt to
+		// be used.
+		prebuilt: []OsType{Android, BuildOs},
+	},
+	{
+		name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=true",
+		modules: `
+			source {
+				name: "bar",
+			}
+
+			prebuilt {
+				name: "bar",
+				use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
+				srcs: ["prebuilt_file"],
+			}`,
+		preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.VendorVars = map[string]map[string]string{
+				"acme": {
+					"use_source": "true",
+				},
+			}
+		}),
+		// Setting the environment variable named in use_source_env to true will cause the source to be
+		// used.
+		prebuilt: nil,
+	},
+	{
+		name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=true, no source",
+		modules: `
+			prebuilt {
+				name: "bar",
+				use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
+				srcs: ["prebuilt_file"],
+			}`,
+		preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.VendorVars = map[string]map[string]string{
+				"acme": {
+					"use_source": "true",
+				},
+			}
+		}),
+		// Although the environment variable says to use source there is no source available.
+		prebuilt: []OsType{Android, BuildOs},
+	},
 }
 
 func TestPrebuilts(t *testing.T) {
@@ -329,6 +410,7 @@
 				}),
 				fs.AddToFixture(),
 				FixtureRegisterWithContext(registerTestPrebuiltModules),
+				OptionalFixturePreparer(test.preparer),
 			).RunTestWithBp(t, bp)
 
 			for _, variant := range result.ModuleVariantsForTests("foo") {
diff --git a/android/testing.go b/android/testing.go
index b36f62c..17a812e 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -110,6 +110,11 @@
 	FixtureAddFile("build/soong/licenses/LICENSE", nil),
 )
 
+var PrepareForTestWithNamespace = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	registerNamespaceBuildComponents(ctx)
+	ctx.PreArchMutators(RegisterNamespaceMutator)
+})
+
 // Test fixture preparer that will register most java build components.
 //
 // Singletons and mutators should only be added here if they are needed for a majority of java
diff --git a/android/variable.go b/android/variable.go
index b6e168c..bbb9868 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -225,6 +225,7 @@
 	Allow_missing_dependencies   *bool `json:",omitempty"`
 	Unbundled_build              *bool `json:",omitempty"`
 	Unbundled_build_apps         *bool `json:",omitempty"`
+	Unbundled_build_image        *bool `json:",omitempty"`
 	Always_use_prebuilt_sdks     *bool `json:",omitempty"`
 	Skip_boot_jars_check         *bool `json:",omitempty"`
 	Malloc_not_svelte            *bool `json:",omitempty"`
diff --git a/apex/Android.bp b/apex/Android.bp
index 6269757..b9b5428 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -9,6 +9,7 @@
         "blueprint",
         "soong",
         "soong-android",
+        "soong-bazel",
         "soong-bpf",
         "soong-cc",
         "soong-filesystem",
diff --git a/apex/apex.go b/apex/apex.go
index baaf874..11df288 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -27,6 +27,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/bpf"
 	"android/soong/cc"
 	prebuilt_etc "android/soong/etc"
@@ -53,6 +54,8 @@
 	ctx.PreArchMutators(registerPreArchMutators)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
+
+	android.RegisterBp2BuildMutator("apex", ApexBundleBp2Build)
 }
 
 func registerPreArchMutators(ctx android.RegisterMutatorsContext) {
@@ -327,6 +330,7 @@
 	android.DefaultableModuleBase
 	android.OverridableModuleBase
 	android.SdkBase
+	android.BazelModuleBase
 
 	// Properties
 	properties            apexBundleProperties
@@ -3178,3 +3182,63 @@
 		},
 	}
 }
+
+// For Bazel / bp2build
+
+type bazelApexBundleAttributes struct {
+	Manifest bazel.LabelAttribute
+}
+
+type bazelApexBundle struct {
+	android.BazelTargetModuleBase
+	bazelApexBundleAttributes
+}
+
+func BazelApexBundleFactory() android.Module {
+	module := &bazelApexBundle{}
+	module.AddProperties(&module.bazelApexBundleAttributes)
+	android.InitBazelTargetModule(module)
+	return module
+}
+
+func ApexBundleBp2Build(ctx android.TopDownMutatorContext) {
+	module, ok := ctx.Module().(*apexBundle)
+	if !ok {
+		// Not an APEX bundle
+		return
+	}
+	if !module.ConvertWithBp2build(ctx) {
+		return
+	}
+	if ctx.ModuleType() != "apex" {
+		return
+	}
+
+	apexBundleBp2BuildInternal(ctx, module)
+}
+
+func apexBundleBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexBundle) {
+	var manifestLabelAttribute bazel.LabelAttribute
+
+	manifestStringPtr := module.properties.Manifest
+	if module.properties.Manifest != nil {
+		manifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *manifestStringPtr))
+	}
+
+	attrs := &bazelApexBundleAttributes{
+		Manifest: manifestLabelAttribute,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "apex",
+		Bzl_load_location: "//build/bazel/rules:apex.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(BazelApexBundleFactory, module.Name(), props, attrs)
+}
+
+func (m *bazelApexBundle) Name() string {
+	return m.BaseModuleName()
+}
+
+func (m *bazelApexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index b5b1d44..422e46c 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4687,16 +4687,26 @@
 		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
 		dexJarBuildPath := p.DexJarBuildPath()
 		stem := android.RemoveOptionalPrebuiltPrefix(name)
-		if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected {
-			t.Errorf("Incorrect DexJarBuildPath value '%s', expected '%s'", actual, expected)
-		}
+		android.AssertStringEquals(t, "DexJarBuildPath should be apex-related path.",
+			".intermediates/myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar",
+			android.NormalizePathForTesting(dexJarBuildPath))
+	}
+
+	checkDexJarInstallPath := func(t *testing.T, ctx *android.TestContext, name string) {
+		// Make sure the import has been given the correct path to the dex jar.
+		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
+		dexJarBuildPath := p.DexJarInstallPath()
+		stem := android.RemoveOptionalPrebuiltPrefix(name)
+		android.AssertStringEquals(t, "DexJarInstallPath should be apex-related path.",
+			"target/product/test_device/apex/myapex/javalib/"+stem+".jar",
+			android.NormalizePathForTesting(dexJarBuildPath))
 	}
 
 	ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext, name string) {
 		// Make sure that an apex variant is not created for the source module.
-		if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests(name); !reflect.DeepEqual(expected, actual) {
-			t.Errorf("invalid set of variants for %q: expected %q, found %q", "libfoo", expected, actual)
-		}
+		android.AssertArrayString(t, "Check if there is no source variant",
+			[]string{"android_common"},
+			ctx.ModuleVariantsForTests(name))
 	}
 
 	t.Run("prebuilt only", func(t *testing.T) {
@@ -4745,8 +4755,10 @@
 		}
 
 		checkDexJarBuildPath(t, ctx, "libfoo")
+		checkDexJarInstallPath(t, ctx, "libfoo")
 
 		checkDexJarBuildPath(t, ctx, "libbar")
+		checkDexJarInstallPath(t, ctx, "libbar")
 	})
 
 	t.Run("prebuilt with source preferred", func(t *testing.T) {
@@ -4792,9 +4804,11 @@
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
 		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
+		checkDexJarInstallPath(t, ctx, "prebuilt_libfoo")
 		ensureNoSourceVariant(t, ctx, "libfoo")
 
 		checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
+		checkDexJarInstallPath(t, ctx, "prebuilt_libbar")
 		ensureNoSourceVariant(t, ctx, "libbar")
 	})
 
@@ -4842,9 +4856,11 @@
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
 		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
+		checkDexJarInstallPath(t, ctx, "prebuilt_libfoo")
 		ensureNoSourceVariant(t, ctx, "libfoo")
 
 		checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
+		checkDexJarInstallPath(t, ctx, "prebuilt_libbar")
 		ensureNoSourceVariant(t, ctx, "libbar")
 	})
 }
diff --git a/bazel/properties.go b/bazel/properties.go
index 0dd47da..7ecc92b 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -558,6 +558,19 @@
 	return len(lla.ConfigurableValues) > 0
 }
 
+// IsEmpty returns true if the attribute has no values under any configuration.
+func (lla LabelListAttribute) IsEmpty() bool {
+	if len(lla.Value.Includes) > 0 {
+		return false
+	}
+	for axis, _ := range lla.ConfigurableValues {
+		if lla.ConfigurableValues[axis].HasConfigurableValues() {
+			return false
+		}
+	}
+	return true
+}
+
 // ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
 // the base value and included in default values as appropriate.
 func (lla *LabelListAttribute) ResolveExcludes() {
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 0e6030e..dded14b 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -19,6 +19,7 @@
     ],
     deps: [
         "soong-android",
+        "soong-apex",
         "soong-bazel",
         "soong-cc",
         "soong-cc-config",
@@ -27,6 +28,7 @@
         "soong-sh",
     ],
     testSrcs: [
+        "apex_conversion_test.go",
         "build_conversion_test.go",
         "bzl_conversion_test.go",
         "cc_library_conversion_test.go",
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
new file mode 100644
index 0000000..fbf6fa2
--- /dev/null
+++ b/bp2build/apex_conversion_test.go
@@ -0,0 +1,48 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/apex"
+	"testing"
+)
+
+func runApexTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerApexModuleTypes, tc)
+}
+
+func registerApexModuleTypes(ctx android.RegistrationContext) {
+}
+
+func TestApexBundleSimple(t *testing.T) {
+	runApexTestCase(t, bp2buildTestCase{
+		description:                        "apex - simple example",
+		moduleTypeUnderTest:                "apex",
+		moduleTypeUnderTestFactory:         apex.BundleFactory,
+		moduleTypeUnderTestBp2BuildMutator: apex.ApexBundleBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: `
+apex {
+	name: "apogee",
+	manifest: "manifest.json",
+}
+`,
+		expectedBazelTargets: []string{`apex(
+    name = "apogee",
+    manifest = "manifest.json",
+)`}})
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 285677a..4f720f5 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -674,6 +674,10 @@
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
+    asflags = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
     copts = [
         "-Ifoo/bar",
         "-I$(BINDIR)/foo/bar",
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 40edec8..c33889f 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1413,7 +1413,7 @@
 
 func TestCcLibraryStaticProductVariableStringReplacement(t *testing.T) {
 	runCcLibraryStaticTestCase(t, bp2buildTestCase{
-		description:                        "cc_library_static product variable selects",
+		description:                        "cc_library_static product variable string replacement",
 		moduleTypeUnderTest:                "cc_library_static",
 		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
@@ -1422,7 +1422,7 @@
 		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
-    srcs: ["common.c"],
+    srcs: ["common.S"],
     product_variables: {
       platform_sdk_version: {
           asflags: ["-DPLATFORM_SDK_VERSION=%d"],
@@ -1431,7 +1431,10 @@
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    asflags = select({
+    asflags = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ] + select({
         "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
         "//conditions:default": [],
     }),
@@ -1440,7 +1443,7 @@
         "-I$(BINDIR)/.",
     ],
     linkstatic = True,
-    srcs_c = ["common.c"],
+    srcs_as = ["common.S"],
 )`},
 	})
 }
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 57f75ea..df4924b 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -46,6 +46,7 @@
 		blueprint: `cc_object {
     name: "foo",
     local_include_dirs: ["include"],
+    default_shared_libs: [],
     cflags: [
         "-Wno-gcc-compat",
         "-Wall",
@@ -83,6 +84,7 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
 		blueprint: `cc_object {
     name: "foo",
+    default_shared_libs: [],
     local_include_dirs: ["include"],
     srcs: [
         "a/b/*.h",
@@ -135,12 +137,14 @@
 		},
 		blueprint: `cc_object {
     name: "foo",
+    default_shared_libs: [],
     srcs: ["a/b/c.c"],
     objs: ["bar"],
 }
 
 cc_object {
     name: "bar",
+    default_shared_libs: [],
     srcs: ["x/y/z.c"],
 }
 `,
@@ -178,6 +182,7 @@
 		},
 		blueprint: `cc_object {
     name: "foo",
+    default_shared_libs: [],
     srcs: ["a/b/c.c"],
     include_build_directory: false,
 }
@@ -199,12 +204,14 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
 		blueprint: `cc_object {
     name: "foo",
+    default_shared_libs: [],
     include_build_directory: false,
     product_variables: {
         platform_sdk_version: {
             asflags: ["-DPLATFORM_SDK_VERSION=%d"],
         },
     },
+    srcs: ["src.S"],
 }
 `,
 		expectedBazelTargets: []string{`cc_object(
@@ -214,6 +221,7 @@
         "//conditions:default": [],
     }),
     copts = ["-fno-addrsig"],
+    srcs_as = ["src.S"],
 )`,
 		},
 	})
@@ -227,13 +235,14 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
 		blueprint: `cc_object {
     name: "foo",
+    default_shared_libs: [],
     srcs: ["a.cpp"],
     arch: {
         x86: {
             cflags: ["-fPIC"], // string list
         },
         arm: {
-            srcs: ["arch/arm/file.S"], // label list
+            srcs: ["arch/arm/file.cpp"], // label list
         },
     },
 }
@@ -250,7 +259,7 @@
         "//conditions:default": [],
     }),
     srcs = ["a.cpp"] + select({
-        "//build/bazel/platforms/arch:arm": ["arch/arm/file.S"],
+        "//build/bazel/platforms/arch:arm": ["arch/arm/file.cpp"],
         "//conditions:default": [],
     }),
 )`,
@@ -266,6 +275,7 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
 		blueprint: `cc_object {
     name: "foo",
+    default_shared_libs: [],
     srcs: ["base.cpp"],
     arch: {
         x86: {
@@ -321,6 +331,7 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
 		blueprint: `cc_object {
     name: "foo",
+    default_shared_libs: [],
     srcs: ["base.cpp"],
     target: {
         android: {
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 9640024..a608630 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -130,7 +130,15 @@
 	},
 	{
 		Name: "removePdkProperty",
-		Fix:  runPatchListMod(removePdkProperty),
+		Fix:  runPatchListMod(removeObsoleteProperty("product_variables.pdk")),
+	},
+	{
+		Name: "removeScudoProperty",
+		Fix:  runPatchListMod(removeObsoleteProperty("sanitize.scudo")),
+	},
+	{
+		Name: "formatFlagProperties",
+		Fix:  runPatchListMod(formatFlagProperties),
 	},
 }
 
@@ -392,7 +400,7 @@
 			continue
 		}
 
-		if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") {
+		if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") && mod.Type != "cc_binary" {
 			continue
 		}
 
@@ -420,6 +428,14 @@
 				mod.Type = "java_test_host"
 			}
 		}
+
+		// when a cc_binary module has a nonempty test_suites field, modify the type to cc_test
+		if mod.Type == "cc_binary" {
+			hasTestSuites := hasNonEmptyLiteralListProperty(mod, "test_suites")
+			if hasTestSuites {
+				mod.Type = "cc_test"
+			}
+		}
 	}
 
 	return nil
@@ -855,7 +871,9 @@
 	}
 }
 
-func runPatchListMod(modFunc func(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error) func(*Fixer) error {
+type patchListModFunction func(*parser.Module, []byte, *parser.PatchList) error
+
+func runPatchListMod(modFunc patchListModFunction) func(*Fixer) error {
 	return func(f *Fixer) error {
 		// Make sure all the offsets are accurate
 		buf, err := f.reparse()
@@ -1025,23 +1043,63 @@
 	return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, replaceStr)
 }
 
-func removePdkProperty(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
-	prop, ok := mod.GetProperty("product_variables")
-	if !ok {
-		return nil
+type propertyProvider interface {
+	GetProperty(string) (*parser.Property, bool)
+	RemoveProperty(string) bool
+}
+
+func removeNestedProperty(mod *parser.Module, patchList *parser.PatchList, propName string) error {
+	propNames := strings.Split(propName, ".")
+
+	var propProvider, toRemoveFrom propertyProvider
+	propProvider = mod
+
+	var propToRemove *parser.Property
+	for i, name := range propNames {
+		p, ok := propProvider.GetProperty(name)
+		if !ok {
+			return nil
+		}
+		// if this is the inner most element, it's time to delete
+		if i == len(propNames)-1 {
+			if propToRemove == nil {
+				// if we cannot remove the properties that the current property is nested in,
+				// remove only the current property
+				propToRemove = p
+				toRemoveFrom = propProvider
+			}
+
+			// remove the property from the list, in case we remove other properties in this list
+			toRemoveFrom.RemoveProperty(propToRemove.Name)
+			// only removing the property would leave blank line(s), remove with a patch
+			if err := patchList.Add(propToRemove.Pos().Offset, propToRemove.End().Offset+2, ""); err != nil {
+				return err
+			}
+		} else {
+			propMap, ok := p.Value.(*parser.Map)
+			if !ok {
+				return nil
+			}
+			if len(propMap.Properties) > 1 {
+				// if there are other properties in this struct, we need to keep this struct
+				toRemoveFrom = nil
+				propToRemove = nil
+			} else if propToRemove == nil {
+				// otherwise, we can remove the empty struct entirely
+				toRemoveFrom = propProvider
+				propToRemove = p
+			}
+			propProvider = propMap
+		}
 	}
-	propMap, ok := prop.Value.(*parser.Map)
-	if !ok {
-		return nil
+
+	return nil
+}
+
+func removeObsoleteProperty(propName string) patchListModFunction {
+	return func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
+		return removeNestedProperty(mod, patchList, propName)
 	}
-	pdkProp, ok := propMap.GetProperty("pdk")
-	if !ok {
-		return nil
-	}
-	if len(propMap.Properties) > 1 {
-		return patchlist.Add(pdkProp.Pos().Offset, pdkProp.End().Offset+2, "")
-	}
-	return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, "")
 }
 
 func mergeMatchingModuleProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
@@ -1289,3 +1347,69 @@
 	}
 	return false
 }
+
+func formatFlagProperty(mod *parser.Module, field string, buf []byte, patchlist *parser.PatchList) error {
+	// the comment or empty lines in the value of the field are skipped
+	listValue, ok := getLiteralListProperty(mod, field)
+	if !ok {
+		// if do not find
+		return nil
+	}
+	for i := 0; i < len(listValue.Values); i++ {
+		curValue, ok := listValue.Values[i].(*parser.String)
+		if !ok {
+			return fmt.Errorf("Expecting string for %s.%s fields", mod.Type, field)
+		}
+		if !strings.HasPrefix(curValue.Value, "-") {
+			return fmt.Errorf("Expecting the string `%s` starting with '-'", curValue.Value)
+		}
+		if i+1 < len(listValue.Values) {
+			nextValue, ok := listValue.Values[i+1].(*parser.String)
+			if !ok {
+				return fmt.Errorf("Expecting string for %s.%s fields", mod.Type, field)
+			}
+			if !strings.HasPrefix(nextValue.Value, "-") {
+				// delete the line
+				err := patchlist.Add(curValue.Pos().Offset, curValue.End().Offset+2, "")
+				if err != nil {
+					return err
+				}
+				// replace the line
+				value := "\"" + curValue.Value + " " + nextValue.Value + "\","
+				err = patchlist.Add(nextValue.Pos().Offset, nextValue.End().Offset+1, value)
+				if err != nil {
+					return err
+				}
+				// combined two lines to one
+				i++
+			}
+		}
+	}
+	return nil
+}
+
+func formatFlagProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+	relevantFields := []string{
+		// cc flags
+		"asflags",
+		"cflags",
+		"clang_asflags",
+		"clang_cflags",
+		"conlyflags",
+		"cppflags",
+		"ldflags",
+		"tidy_flags",
+		// java flags
+		"aaptflags",
+		"dxflags",
+		"javacflags",
+		"kotlincflags",
+	}
+	for _, field := range relevantFields {
+		err := formatFlagProperty(mod, field, buf, patchlist)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index ebfeb22..d8772c1 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -999,7 +999,171 @@
 	}
 }
 
-func TestRemovePdkProperty(t *testing.T) {
+func TestRemoveNestedProperty(t *testing.T) {
+	tests := []struct {
+		name         string
+		in           string
+		out          string
+		propertyName string
+	}{
+		{
+			name: "remove no nesting",
+			in: `
+cc_library {
+	name: "foo",
+	foo: true,
+}`,
+			out: `
+cc_library {
+	name: "foo",
+}
+`,
+			propertyName: "foo",
+		},
+		{
+			name: "remove one nest",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: true,
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+}
+`,
+			propertyName: "foo.bar",
+		},
+		{
+			name: "remove one nest, multiple props",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: true,
+		baz: false,
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+	foo: {
+		baz: false,
+	},
+}
+`,
+			propertyName: "foo.bar",
+		},
+		{
+			name: "remove multiple nest",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			baz: {
+				a: true,
+			}
+		},
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+}
+`,
+			propertyName: "foo.bar.baz.a",
+		},
+		{
+			name: "remove multiple nest, outer non-empty",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			baz: {
+				a: true,
+			}
+		},
+		other: true,
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+	foo: {
+		other: true,
+	},
+}
+`,
+			propertyName: "foo.bar.baz.a",
+		},
+		{
+			name: "remove multiple nest, inner non-empty",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			baz: {
+				a: true,
+			},
+			other: true,
+		},
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			other: true,
+		},
+	},
+}
+`,
+			propertyName: "foo.bar.baz.a",
+		},
+		{
+			name: "remove multiple nest, inner-most non-empty",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			baz: {
+				a: true,
+				other: true,
+			},
+		},
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			baz: {
+				other: true,
+			},
+		},
+	},
+}
+`,
+			propertyName: "foo.bar.baz.a",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, runPatchListMod(removeObsoleteProperty(test.propertyName)))
+		})
+	}
+}
+
+func TestRemoveObsoleteProperties(t *testing.T) {
 	tests := []struct {
 		name string
 		in   string
@@ -1052,7 +1216,7 @@
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			runPass(t, test.in, test.out, runPatchListMod(removePdkProperty))
+			runPass(t, test.in, test.out, runPatchListMod(removeObsoleteProperty("product_variables.pdk")))
 		})
 	}
 }
@@ -1124,3 +1288,323 @@
 		})
 	}
 }
+
+func TestRewriteTestModuleTypes(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "cc_binary with test_suites",
+			in: `
+				cc_binary {
+					name: "foo",
+					srcs: ["srcs"],
+					test_suites: ["test_suite1"],
+				}
+			`,
+			out: `
+				cc_test {
+					name: "foo",
+					srcs: ["srcs"],
+					test_suites: ["test_suite1"],
+				}
+			`,
+		},
+		{
+			name: "cc_binary without test_suites",
+			in: `
+				cc_binary {
+					name: "foo",
+					srcs: ["srcs"],
+				}
+			`,
+			out: `
+				cc_binary {
+					name: "foo",
+					srcs: ["srcs"],
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, func(fixer *Fixer) error {
+				return rewriteTestModuleTypes(fixer)
+			})
+		})
+	}
+}
+
+func TestFormatFlagProperty(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "group options and values for apptflags, dxflags, javacflags, and kotlincflags",
+			in: `
+				android_test {
+					name: "foo",
+					aaptflags: [
+						// comment1_1
+						"--flag1",
+						// comment1_2
+						"1",
+						// comment2_1
+						// comment2_2
+						"--flag2",
+						// comment3_1
+						// comment3_2
+						// comment3_3
+						"--flag3",
+						// comment3_4
+						// comment3_5
+						// comment3_6
+						"3",
+						// other comment1_1
+						// other comment1_2
+					],
+					dxflags: [
+						"--flag1",
+						// comment1_1
+						"1",
+						// comment2_1
+						"--flag2",
+						// comment3_1
+						"--flag3",
+						// comment3_2
+						"3",
+					],
+					javacflags: [
+						"--flag1",
+
+						"1",
+						"--flag2",
+						"--flag3",
+						"3",
+					],
+					kotlincflags: [
+
+						"--flag1",
+						"1",
+
+						"--flag2",
+						"--flag3",
+						"3",
+
+					],
+				}
+			`,
+			out: `
+				android_test {
+					name: "foo",
+					aaptflags: [
+						// comment1_1
+						// comment1_2
+						"--flag1 1",
+						// comment2_1
+						// comment2_2
+						"--flag2",
+						// comment3_1
+						// comment3_2
+						// comment3_3
+						// comment3_4
+						// comment3_5
+						// comment3_6
+						"--flag3 3",
+						// other comment1_1
+						// other comment1_2
+					],
+					dxflags: [
+						// comment1_1
+						"--flag1 1",
+						// comment2_1
+						"--flag2",
+						// comment3_1
+						// comment3_2
+						"--flag3 3",
+					],
+					javacflags: [
+
+						"--flag1 1",
+						"--flag2",
+						"--flag3 3",
+					],
+					kotlincflags: [
+
+						"--flag1 1",
+
+						"--flag2",
+						"--flag3 3",
+
+					],
+				}
+			`,
+		},
+		{
+			name: "group options and values for asflags, cflags, clang_asflags, clang_cflags, conlyflags, cppflags, ldflags, and tidy_flags",
+			in: `
+				cc_test {
+					name: "foo",
+					asflags: [
+						// comment1_1
+						"--flag1",
+						"1",
+						// comment2_1
+						// comment2_2
+						"--flag2",
+						// comment2_3
+						"2",
+						// comment3_1
+						// comment3_2
+						"--flag3",
+						// comment3_3
+						// comment3_4
+						// comment3_4
+						"3",
+						// comment4_1
+						// comment4_2
+						// comment4_3
+						"--flag4",
+					],
+					cflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					clang_asflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					clang_cflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					conlyflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					cppflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					ldflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					tidy_flags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+				}
+			`,
+			out: `
+				cc_test {
+					name: "foo",
+					asflags: [
+						// comment1_1
+						"--flag1 1",
+						// comment2_1
+						// comment2_2
+						// comment2_3
+						"--flag2 2",
+						// comment3_1
+						// comment3_2
+						// comment3_3
+						// comment3_4
+						// comment3_4
+						"--flag3 3",
+						// comment4_1
+						// comment4_2
+						// comment4_3
+						"--flag4",
+					],
+					cflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					clang_asflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					clang_cflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					conlyflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					cppflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					ldflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					tidy_flags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, runPatchListMod(formatFlagProperties))
+		})
+	}
+}
diff --git a/cc/binary.go b/cc/binary.go
index 48f70d9..c177a08 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -183,11 +183,6 @@
 	return deps
 }
 
-func (binary *binaryDecorator) isDependencyRoot() bool {
-	// Binaries are always the dependency root.
-	return true
-}
-
 // NewBinary builds and returns a new Module corresponding to a C++ binary.
 // Individual module implementations which comprise a C++ binary should call this function,
 // set some fields on the result, and then call the Init function.
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 76c5f3b..536f112 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -90,8 +90,11 @@
 
 		// TODO(b/186024507, b/186489250): Temporarily exclude adding
 		// system_shared_libs deps until libc and libm builds.
-		// allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...)
-		// allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...)
+		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...)
+		}
 
 		// Deps in the target/arch nested static: { .. } and shared: { .. } props of a cc_library.
 		// target: { <target>: shared: { ... } }
@@ -253,7 +256,7 @@
 		Copts:              bazel.StringListAttribute{Value: props.Cflags},
 		Srcs:               bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)),
 		Static_deps:        bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)),
-		Dynamic_deps:       bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Shared_libs)),
+		Dynamic_deps:       bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, append(props.Shared_libs, props.System_shared_libs...))),
 		Whole_archive_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs)),
 	}
 
@@ -385,16 +388,6 @@
 		return result
 	}
 
-	// Parse the list of copts.
-	parseCopts := func(baseCompilerProps *BaseCompilerProperties) []string {
-		var copts []string
-		copts = append(copts, parseCommandLineFlags(baseCompilerProps.Cflags)...)
-		for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
-			copts = append(copts, includeFlags(dir)...)
-		}
-		return copts
-	}
-
 	// Parse srcs from an arch or OS's props value.
 	parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList {
 		// Add srcs-like dependencies such as generated files.
@@ -410,11 +403,15 @@
 	for _, props := range module.compiler.compilerProps() {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
 			srcs.SetValue(parseSrcs(baseCompilerProps))
-			copts.Value = parseCopts(baseCompilerProps)
+			copts.Value = parseCommandLineFlags(baseCompilerProps.Cflags)
 			asFlags.Value = parseCommandLineFlags(baseCompilerProps.Asflags)
 			conlyFlags.Value = parseCommandLineFlags(baseCompilerProps.Conlyflags)
 			cppFlags.Value = parseCommandLineFlags(baseCompilerProps.Cppflags)
 
+			for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
+				copts.Value = append(copts.Value, includeFlags(dir)...)
+				asFlags.Value = append(asFlags.Value, includeFlags(dir)...)
+			}
 			break
 		}
 	}
@@ -424,8 +421,10 @@
 	// "-I<module-dir>" in its copts.
 	if c, ok := module.compiler.(*baseCompiler); ok && c.includeBuildDirectory() {
 		copts.Value = append(copts.Value, includeFlags(".")...)
+		asFlags.Value = append(asFlags.Value, includeFlags(".")...)
 	} else if c, ok := module.compiler.(*libraryDecorator); ok && c.includeBuildDirectory() {
 		copts.Value = append(copts.Value, includeFlags(".")...)
+		asFlags.Value = append(asFlags.Value, includeFlags(".")...)
 	}
 
 	archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
@@ -440,8 +439,15 @@
 					srcs.SetSelectValue(axis, config, srcsList)
 				}
 
-				copts.SetSelectValue(axis, config, parseCopts(baseCompilerProps))
-				asFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Asflags))
+				archVariantCopts := parseCommandLineFlags(baseCompilerProps.Cflags)
+				archVariantAsflags := parseCommandLineFlags(baseCompilerProps.Asflags)
+				for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
+					archVariantCopts = append(archVariantCopts, includeFlags(dir)...)
+					archVariantAsflags = append(archVariantAsflags, includeFlags(dir)...)
+				}
+
+				copts.SetSelectValue(axis, config, archVariantCopts)
+				asFlags.SetSelectValue(axis, config, archVariantAsflags)
 				conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Conlyflags))
 				cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Cppflags))
 			}
@@ -554,7 +560,9 @@
 			staticDeps.Value = android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs)
 			wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
 			wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
-			sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
+			// TODO(b/186024507): Handle system_shared_libs as its own attribute, so that the appropriate default
+			// may be supported.
+			sharedLibs := android.FirstUniqueStrings(append(baseLinkerProps.Shared_libs, baseLinkerProps.System_shared_libs...))
 			dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
 
 			headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
@@ -581,7 +589,7 @@
 				staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs))
 				wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
 				wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
-				sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
+				sharedLibs := android.FirstUniqueStrings(append(baseLinkerProps.Shared_libs, baseLinkerProps.System_shared_libs...))
 				dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
 
 				headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
diff --git a/cc/cc.go b/cc/cc.go
index 0aa1014..aeebaef 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -53,25 +53,9 @@
 	})
 
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("asan_deps", sanitizerDepsMutator(Asan))
-		ctx.BottomUp("asan", sanitizerMutator(Asan)).Parallel()
-
-		ctx.TopDown("hwasan_deps", sanitizerDepsMutator(Hwasan))
-		ctx.BottomUp("hwasan", sanitizerMutator(Hwasan)).Parallel()
-
-		ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(Fuzzer))
-		ctx.BottomUp("fuzzer", sanitizerMutator(Fuzzer)).Parallel()
-
-		// cfi mutator shouldn't run before sanitizers that return true for
-		// incompatibleWithCfi()
-		ctx.TopDown("cfi_deps", sanitizerDepsMutator(cfi))
-		ctx.BottomUp("cfi", sanitizerMutator(cfi)).Parallel()
-
-		ctx.TopDown("scs_deps", sanitizerDepsMutator(scs))
-		ctx.BottomUp("scs", sanitizerMutator(scs)).Parallel()
-
-		ctx.TopDown("tsan_deps", sanitizerDepsMutator(tsan))
-		ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel()
+		for _, san := range Sanitizers {
+			san.registerMutators(ctx)
+		}
 
 		ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
 		ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
@@ -337,6 +321,7 @@
 
 	// Used by vendor snapshot to record dependencies from snapshot modules.
 	SnapshotSharedLibs  []string `blueprint:"mutated"`
+	SnapshotStaticLibs  []string `blueprint:"mutated"`
 	SnapshotRuntimeLibs []string `blueprint:"mutated"`
 
 	Installable *bool
@@ -525,8 +510,6 @@
 // feature represents additional (optional) steps to building cc-related modules, such as invocation
 // of clang-tidy.
 type feature interface {
-	begin(ctx BaseModuleContext)
-	deps(ctx DepsContext, deps Deps) Deps
 	flags(ctx ModuleContext, flags Flags) Flags
 	props() []interface{}
 }
@@ -1120,17 +1103,6 @@
 	return c
 }
 
-// Returns true for dependency roots (binaries)
-// TODO(ccross): also handle dlopenable libraries
-func (c *Module) IsDependencyRoot() bool {
-	if root, ok := c.linker.(interface {
-		isDependencyRoot() bool
-	}); ok {
-		return root.isDependencyRoot()
-	}
-	return false
-}
-
 func (c *Module) UseVndk() bool {
 	return c.Properties.VndkVersion != ""
 }
@@ -1898,21 +1870,12 @@
 	if c.coverage != nil {
 		c.coverage.begin(ctx)
 	}
-	if c.sabi != nil {
-		c.sabi.begin(ctx)
-	}
-	if c.vndkdep != nil {
-		c.vndkdep.begin(ctx)
-	}
 	if c.lto != nil {
 		c.lto.begin(ctx)
 	}
 	if c.pgo != nil {
 		c.pgo.begin(ctx)
 	}
-	for _, feature := range c.features {
-		feature.begin(ctx)
-	}
 	if ctx.useSdk() && c.IsSdkVariant() {
 		version, err := nativeApiLevelFromUser(ctx, ctx.sdkVersion())
 		if err != nil {
@@ -1936,24 +1899,9 @@
 	if c.stl != nil {
 		deps = c.stl.deps(ctx, deps)
 	}
-	if c.sanitize != nil {
-		deps = c.sanitize.deps(ctx, deps)
-	}
 	if c.coverage != nil {
 		deps = c.coverage.deps(ctx, deps)
 	}
-	if c.sabi != nil {
-		deps = c.sabi.deps(ctx, deps)
-	}
-	if c.vndkdep != nil {
-		deps = c.vndkdep.deps(ctx, deps)
-	}
-	if c.lto != nil {
-		deps = c.lto.deps(ctx, deps)
-	}
-	for _, feature := range c.features {
-		deps = feature.deps(ctx, deps)
-	}
 
 	deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
 	deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
@@ -2066,9 +2014,9 @@
 }
 
 func GetSnapshot(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.BottomUpMutatorContext) SnapshotInfo {
-	// Only modules with BOARD_VNDK_VERSION uses snapshot.  Others use the zero value of
+	// Only device modules with BOARD_VNDK_VERSION uses snapshot.  Others use the zero value of
 	// SnapshotInfo, which provides no mappings.
-	if *snapshotInfo == nil {
+	if *snapshotInfo == nil && c.Device() {
 		// Only retrieve the snapshot on demand in order to avoid circular dependencies
 		// between the modules in the snapshot and the snapshot itself.
 		var snapshotModule []blueprint.Module
@@ -2077,16 +2025,16 @@
 		} else if recoverySnapshotVersion := actx.DeviceConfig().RecoverySnapshotVersion(); recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" && c.InRecovery() {
 			snapshotModule = actx.AddVariationDependencies(nil, nil, "recovery_snapshot")
 		}
-		if len(snapshotModule) > 0 {
+		if len(snapshotModule) > 0 && snapshotModule[0] != nil {
 			snapshot := actx.OtherModuleProvider(snapshotModule[0], SnapshotInfoProvider).(SnapshotInfo)
 			*snapshotInfo = &snapshot
 			// republish the snapshot for use in later mutators on this module
 			actx.SetProvider(SnapshotInfoProvider, snapshot)
-		} else {
-			*snapshotInfo = &SnapshotInfo{}
 		}
 	}
-
+	if *snapshotInfo == nil {
+		*snapshotInfo = &SnapshotInfo{}
+	}
 	return **snapshotInfo
 }
 
@@ -2856,6 +2804,8 @@
 					c.Properties.AndroidMkStaticLibs = append(
 						c.Properties.AndroidMkStaticLibs, makeLibName)
 				}
+				// Record BaseLibName for snapshots.
+				c.Properties.SnapshotStaticLibs = append(c.Properties.SnapshotStaticLibs, BaseLibName(depName))
 			}
 		} else if !c.IsStubs() {
 			// Stubs lib doesn't link to the runtime lib, object, crt, etc. dependencies.
@@ -3179,6 +3129,13 @@
 	return false
 }
 
+func (c *Module) StaticExecutable() bool {
+	if b, ok := c.linker.(*binaryDecorator); ok {
+		return b.static()
+	}
+	return false
+}
+
 func (c *Module) Object() bool {
 	if o, ok := c.linker.(interface {
 		object() bool
diff --git a/cc/library.go b/cc/library.go
index 4fd7c74..56c460c 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -300,6 +300,12 @@
 
 	srcs := compilerAttrs.srcs
 
+	asFlags := compilerAttrs.asFlags
+	if compilerAttrs.asSrcs.IsEmpty() && sharedAttrs.Srcs_as.IsEmpty() && staticAttrs.Srcs_as.IsEmpty() {
+		// Skip asflags for BUILD file simplicity if there are no assembly sources.
+		asFlags = bazel.MakeStringListAttribute(nil)
+	}
+
 	attrs := &bazelCcLibraryAttributes{
 		Srcs:    srcs,
 		Srcs_c:  compilerAttrs.cSrcs,
@@ -308,7 +314,7 @@
 		Copts:      compilerAttrs.copts,
 		Cppflags:   compilerAttrs.cppFlags,
 		Conlyflags: compilerAttrs.conlyFlags,
-		Asflags:    compilerAttrs.asFlags,
+		Asflags:    asFlags,
 
 		Implementation_deps: linkerAttrs.deps,
 		Deps:                linkerAttrs.exportedDeps,
@@ -2370,6 +2376,12 @@
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
 
+	asFlags := compilerAttrs.asFlags
+	if compilerAttrs.asSrcs.IsEmpty() {
+		// Skip asflags for BUILD file simplicity if there are no assembly sources.
+		asFlags = bazel.MakeStringListAttribute(nil)
+	}
+
 	attrs := &bazelCcLibraryStaticAttributes{
 		Copts:               compilerAttrs.copts,
 		Srcs:                compilerAttrs.srcs,
@@ -2386,7 +2398,7 @@
 		Srcs_c:     compilerAttrs.cSrcs,
 		Conlyflags: compilerAttrs.conlyFlags,
 		Srcs_as:    compilerAttrs.asSrcs,
-		Asflags:    compilerAttrs.asFlags,
+		Asflags:    asFlags,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/linkable.go b/cc/linkable.go
index 231626e..6232efb 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -14,11 +14,6 @@
 	// SanitizePropDefined returns whether the Sanitizer properties struct for this module is defined.
 	SanitizePropDefined() bool
 
-	// IsDependencyRoot returns whether a module is of a type which cannot be a linkage dependency
-	// of another module. For example, cc_binary and rust_binary represent dependency roots as other
-	// modules cannot have linkage dependencies against these types.
-	IsDependencyRoot() bool
-
 	// IsSanitizerEnabled returns whether a sanitizer is enabled.
 	IsSanitizerEnabled(t SanitizerType) bool
 
@@ -88,12 +83,18 @@
 	// SnapshotLibrary returns true if this module is a snapshot library.
 	IsSnapshotLibrary() bool
 
+	// EffectiveLicenseFiles returns the list of License files for this module.
+	EffectiveLicenseFiles() android.Paths
+
 	// SnapshotRuntimeLibs returns a list of libraries needed by this module at runtime but which aren't build dependencies.
 	SnapshotRuntimeLibs() []string
 
 	// SnapshotSharedLibs returns the list of shared library dependencies for this module.
 	SnapshotSharedLibs() []string
 
+	// SnapshotStaticLibs returns the list of static library dependencies for this module.
+	SnapshotStaticLibs() []string
+
 	// IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt.
 	IsSnapshotPrebuilt() bool
 }
@@ -124,6 +125,7 @@
 	IsPrebuilt() bool
 	Toc() android.OptionalPath
 
+	Device() bool
 	Host() bool
 
 	InRamdisk() bool
@@ -226,6 +228,9 @@
 	// Header returns true if this is a library headers module.
 	Header() bool
 
+	// StaticExecutable returns true if this is a binary module with "static_executable: true".
+	StaticExecutable() bool
+
 	// EverInstallable returns true if the module is ever installable
 	EverInstallable() bool
 
diff --git a/cc/lto.go b/cc/lto.go
index a3b28d9..d9a0118 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -15,6 +15,8 @@
 package cc
 
 import (
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 )
 
@@ -67,23 +69,19 @@
 
 func (lto *lto) begin(ctx BaseModuleContext) {
 	if ctx.Config().IsEnvTrue("DISABLE_LTO") {
-		lto.Properties.Lto.Never = boolPtr(true)
+		lto.Properties.Lto.Never = proptools.BoolPtr(true)
 	} else if ctx.Config().IsEnvTrue("GLOBAL_THINLTO") {
 		staticLib := ctx.static() && !ctx.staticBinary()
 		hostBin := ctx.Host()
 		vndk := ctx.isVndk() // b/169217596
 		if !staticLib && !hostBin && !vndk {
 			if !lto.Never() && !lto.FullLTO() {
-				lto.Properties.Lto.Thin = boolPtr(true)
+				lto.Properties.Lto.Thin = proptools.BoolPtr(true)
 			}
 		}
 	}
 }
 
-func (lto *lto) deps(ctx BaseModuleContext, deps Deps) Deps {
-	return deps
-}
-
 func (lto *lto) useClangLld(ctx BaseModuleContext) bool {
 	if lto.Properties.Use_clang_lld != nil {
 		return Bool(lto.Properties.Use_clang_lld)
@@ -233,12 +231,12 @@
 
 				// LTO properties for dependencies
 				if name == "lto-full" {
-					variation.lto.Properties.Lto.Full = boolPtr(true)
-					variation.lto.Properties.Lto.Thin = boolPtr(false)
+					variation.lto.Properties.Lto.Full = proptools.BoolPtr(true)
+					variation.lto.Properties.Lto.Thin = proptools.BoolPtr(false)
 				}
 				if name == "lto-thin" {
-					variation.lto.Properties.Lto.Full = boolPtr(false)
-					variation.lto.Properties.Lto.Thin = boolPtr(true)
+					variation.lto.Properties.Lto.Full = proptools.BoolPtr(false)
+					variation.lto.Properties.Lto.Thin = proptools.BoolPtr(true)
 				}
 				variation.Properties.PreventInstall = true
 				variation.Properties.HideFromMake = true
diff --git a/cc/object.go b/cc/object.go
index 39fc43d..5ded0e9 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -67,9 +67,19 @@
 }
 
 type ObjectLinkerProperties struct {
+	// list of static library modules that should only provide headers for this module.
+	Static_libs []string `android:"arch_variant,variant_prepend"`
+
+	// list of shared library modules should only provide headers for this module.
+	Shared_libs []string `android:"arch_variant"`
+
 	// list of modules that should only provide headers for this module.
 	Header_libs []string `android:"arch_variant,variant_prepend"`
 
+	// list of default libraries that will provide headers for this module.  If unset, generally
+	// defaults to libc, libm, and libdl.  Set to [] to prevent using headers from the defaults.
+	Default_shared_libs []string `android:"arch_variant"`
+
 	// names of other cc_object modules to link into this module using partial linking
 	Objs []string `android:"arch_variant"`
 
@@ -84,8 +94,8 @@
 	Crt *bool
 }
 
-func newObject() *Module {
-	module := newBaseModule(android.HostAndDeviceSupported, android.MultilibBoth)
+func newObject(hod android.HostOrDeviceSupported) *Module {
+	module := newBaseModule(hod, android.MultilibBoth)
 	module.sanitize = &sanitize{}
 	module.stl = &stl{}
 	return module
@@ -95,7 +105,7 @@
 // necessary, but sometimes used to generate .s files from .c files to use as
 // input to a cc_genrule module.
 func ObjectFactory() android.Module {
-	module := newObject()
+	module := newObject(android.HostAndDeviceSupported)
 	module.linker = &objectLinker{
 		baseLinker: NewBaseLinker(module.sanitize),
 	}
@@ -113,6 +123,7 @@
 // For bp2build conversion.
 type bazelObjectAttributes struct {
 	Srcs    bazel.LabelListAttribute
+	Srcs_as bazel.LabelListAttribute
 	Hdrs    bazel.LabelListAttribute
 	Deps    bazel.LabelListAttribute
 	Copts   bazel.StringListAttribute
@@ -169,13 +180,19 @@
 	// and this isn't typically done for cc_object.
 	srcs := compilerAttrs.srcs
 	srcs.Append(compilerAttrs.cSrcs)
-	srcs.Append(compilerAttrs.asSrcs)
+
+	asFlags := compilerAttrs.asFlags
+	if compilerAttrs.asSrcs.IsEmpty() {
+		// Skip asflags for BUILD file simplicity if there are no assembly sources.
+		asFlags = bazel.MakeStringListAttribute(nil)
+	}
 
 	attrs := &bazelObjectAttributes{
 		Srcs:    srcs,
+		Srcs_as: compilerAttrs.asSrcs,
 		Deps:    deps,
 		Copts:   compilerAttrs.copts,
-		Asflags: compilerAttrs.asFlags,
+		Asflags: asFlags,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
@@ -198,7 +215,18 @@
 
 func (object *objectLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	deps.HeaderLibs = append(deps.HeaderLibs, object.Properties.Header_libs...)
+	deps.SharedLibs = append(deps.SharedLibs, object.Properties.Shared_libs...)
+	deps.StaticLibs = append(deps.StaticLibs, object.Properties.Static_libs...)
 	deps.ObjFiles = append(deps.ObjFiles, object.Properties.Objs...)
+
+	deps.SystemSharedLibs = object.Properties.Default_shared_libs
+	if deps.SystemSharedLibs == nil {
+		// Provide a default set of shared libraries if default_shared_libs is unspecified.
+		// Note: If an empty list [] is specified, it implies that the module declines the
+		// default shared libraries.
+		deps.SystemSharedLibs = append(deps.SystemSharedLibs, ctx.toolchain().DefaultSharedLibraries()...)
+	}
+	deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...)
 	return deps
 }
 
@@ -247,6 +275,20 @@
 	return outputFile
 }
 
+func (object *objectLinker) linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps {
+	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, object.Properties.Shared_libs...)
+
+	// Must distinguish nil and [] in default_shared_libs - ensure that [] in
+	// either input list doesn't come out as nil.
+	if specifiedDeps.defaultSharedLibs == nil {
+		specifiedDeps.defaultSharedLibs = object.Properties.Default_shared_libs
+	} else {
+		specifiedDeps.defaultSharedLibs = append(specifiedDeps.defaultSharedLibs, object.Properties.Default_shared_libs...)
+	}
+
+	return specifiedDeps
+}
+
 func (object *objectLinker) unstrippedOutputFilePath() android.Path {
 	return nil
 }
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index fd310a2..f7154ec 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -388,8 +388,8 @@
 	return true
 }
 
-func newPrebuiltObject() *Module {
-	module := newObject()
+func NewPrebuiltObject(hod android.HostOrDeviceSupported) *Module {
+	module := newObject(hod)
 	prebuilt := &prebuiltObjectLinker{
 		objectLinker: objectLinker{
 			baseLinker: NewBaseLinker(nil),
@@ -403,7 +403,7 @@
 }
 
 func prebuiltObjectFactory() android.Module {
-	module := newPrebuiltObject()
+	module := NewPrebuiltObject(android.HostAndDeviceSupported)
 	return module.Init()
 }
 
diff --git a/cc/sabi.go b/cc/sabi.go
index 1f331cb..5fd6f5d 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -45,12 +45,6 @@
 	return []interface{}{&sabi.Properties}
 }
 
-func (sabi *sabi) begin(ctx BaseModuleContext) {}
-
-func (sabi *sabi) deps(ctx BaseModuleContext, deps Deps) Deps {
-	return deps
-}
-
 func (sabi *sabi) flags(ctx ModuleContext, flags Flags) Flags {
 	// Filter out flags which libTooling don't understand.
 	// This is here for legacy reasons and future-proof, in case the version of libTooling and clang
diff --git a/cc/sanitize.go b/cc/sanitize.go
index b0eb0c6..defe8fd 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -21,6 +21,7 @@
 	"sync"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 	"android/soong/cc/config"
@@ -73,25 +74,28 @@
 
 type SanitizerType int
 
-func boolPtr(v bool) *bool {
-	if v {
-		return &v
-	} else {
-		return nil
-	}
-}
-
 const (
 	Asan SanitizerType = iota + 1
 	Hwasan
 	tsan
 	intOverflow
-	cfi
 	scs
 	Fuzzer
 	memtag_heap
+	cfi // cfi is last to prevent it running before incompatible mutators
 )
 
+var Sanitizers = []SanitizerType{
+	Asan,
+	Hwasan,
+	tsan,
+	intOverflow,
+	scs,
+	Fuzzer,
+	memtag_heap,
+	cfi, // cfi is last to prevent it running before incompatible mutators
+}
+
 // Name of the sanitizer variation for this sanitizer type
 func (t SanitizerType) variationName() string {
 	switch t {
@@ -140,6 +144,18 @@
 	}
 }
 
+func (t SanitizerType) registerMutators(ctx android.RegisterMutatorsContext) {
+	switch t {
+	case Asan, Hwasan, Fuzzer, scs, tsan, cfi:
+		ctx.TopDown(t.variationName()+"_deps", sanitizerDepsMutator(t))
+		ctx.BottomUp(t.variationName(), sanitizerMutator(t))
+	case memtag_heap, intOverflow:
+		// do nothing
+	default:
+		panic(fmt.Errorf("unknown SanitizerType %d", t))
+	}
+}
+
 func (*Module) SanitizerSupported(t SanitizerType) bool {
 	switch t {
 	case Asan:
@@ -167,24 +183,46 @@
 }
 
 type SanitizeUserProps struct {
+	// Prevent use of any sanitizers on this module
 	Never *bool `android:"arch_variant"`
 
-	// main sanitizers
-	Address   *bool `android:"arch_variant"`
-	Thread    *bool `android:"arch_variant"`
+	// ASan (Address sanitizer), incompatible with static binaries.
+	// Always runs in a diagnostic mode.
+	// Use of address sanitizer disables cfi sanitizer.
+	// Hwaddress sanitizer takes precedence over this sanitizer.
+	Address *bool `android:"arch_variant"`
+	// TSan (Thread sanitizer), incompatible with static binaries and 32 bit architectures.
+	// Always runs in a diagnostic mode.
+	// Use of thread sanitizer disables cfi and scudo sanitizers.
+	// Hwaddress sanitizer takes precedence over this sanitizer.
+	Thread *bool `android:"arch_variant"`
+	// HWASan (Hardware Address sanitizer).
+	// Use of hwasan sanitizer disables cfi, address, thread, and scudo sanitizers.
 	Hwaddress *bool `android:"arch_variant"`
 
-	// local sanitizers
-	Undefined        *bool    `android:"arch_variant"`
-	All_undefined    *bool    `android:"arch_variant"`
-	Misc_undefined   []string `android:"arch_variant"`
-	Fuzzer           *bool    `android:"arch_variant"`
-	Safestack        *bool    `android:"arch_variant"`
-	Cfi              *bool    `android:"arch_variant"`
-	Integer_overflow *bool    `android:"arch_variant"`
-	Scudo            *bool    `android:"arch_variant"`
-	Scs              *bool    `android:"arch_variant"`
-	Memtag_heap      *bool    `android:"arch_variant"`
+	// Undefined behavior sanitizer
+	All_undefined *bool `android:"arch_variant"`
+	// Subset of undefined behavior sanitizer
+	Undefined *bool `android:"arch_variant"`
+	// List of specific undefined behavior sanitizers to enable
+	Misc_undefined []string `android:"arch_variant"`
+	// Fuzzer, incompatible with static binaries.
+	Fuzzer *bool `android:"arch_variant"`
+	// safe-stack sanitizer, incompatible with 32-bit architectures.
+	Safestack *bool `android:"arch_variant"`
+	// cfi sanitizer, incompatible with asan, hwasan, fuzzer, or Darwin
+	Cfi *bool `android:"arch_variant"`
+	// signed/unsigned integer overflow sanitizer, incompatible with Darwin.
+	Integer_overflow *bool `android:"arch_variant"`
+	// scudo sanitizer, incompatible with asan, hwasan, tsan
+	// This should not be used in Android 11+ : https://source.android.com/devices/tech/debug/scudo
+	// deprecated
+	Scudo *bool `android:"arch_variant"`
+	// shadow-call-stack sanitizer, only available on arm64
+	Scs *bool `android:"arch_variant"`
+	// Memory-tagging, only available on arm64
+	// if diag.memtag unset or false, enables async memory tagging
+	Memtag_heap *bool `android:"arch_variant"`
 
 	// A modifier for ASAN and HWASAN for write only instrumentation
 	Writeonly *bool `android:"arch_variant"`
@@ -193,12 +231,22 @@
 	// Replaces abort() on error with a human-readable error message.
 	// Address and Thread sanitizers always run in diagnostic mode.
 	Diag struct {
-		Undefined        *bool    `android:"arch_variant"`
-		Cfi              *bool    `android:"arch_variant"`
-		Integer_overflow *bool    `android:"arch_variant"`
-		Memtag_heap      *bool    `android:"arch_variant"`
-		Misc_undefined   []string `android:"arch_variant"`
-		No_recover       []string `android:"arch_variant"`
+		// Undefined behavior sanitizer, diagnostic mode
+		Undefined *bool `android:"arch_variant"`
+		// cfi sanitizer, diagnostic mode, incompatible with asan, hwasan, fuzzer, or Darwin
+		Cfi *bool `android:"arch_variant"`
+		// signed/unsigned integer overflow sanitizer, diagnostic mode, incompatible with Darwin.
+		Integer_overflow *bool `android:"arch_variant"`
+		// Memory-tagging, only available on arm64
+		// requires sanitizer.memtag: true
+		// if set, enables sync memory tagging
+		Memtag_heap *bool `android:"arch_variant"`
+		// List of specific undefined behavior sanitizers to enable in diagnostic mode
+		Misc_undefined []string `android:"arch_variant"`
+		// List of sanitizers to pass to -fno-sanitize-recover
+		// results in only the first detected error for these sanitizers being reported and program then
+		// exits with a non-zero exit code.
+		No_recover []string `android:"arch_variant"`
 	} `android:"arch_variant"`
 
 	// Sanitizers to run with flag configuration specified
@@ -207,7 +255,9 @@
 		Cfi_assembly_support *bool `android:"arch_variant"`
 	} `android:"arch_variant"`
 
-	// value to pass to -fsanitize-recover=
+	// List of sanitizers to pass to -fsanitize-recover
+	// allows execution to continue for these sanitizers to detect multiple errors rather than only
+	// the first one
 	Recover []string
 
 	// value to pass to -fsanitize-blacklist
@@ -215,9 +265,7 @@
 }
 
 type SanitizeProperties struct {
-	// Enable AddressSanitizer, ThreadSanitizer, UndefinedBehaviorSanitizer, and
-	// others. Please see SanitizerUserProps in build/soong/cc/sanitize.go for
-	// details.
+	// Sanitizers are not supported for Fuchsia.
 	Sanitize          SanitizeUserProps `android:"arch_variant"`
 	SanitizerEnabled  bool              `blueprint:"mutated"`
 	SanitizeDep       bool              `blueprint:"mutated"`
@@ -268,9 +316,13 @@
 	}
 
 	// cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap}).
-	if ctx.testBinary() && s.Memtag_heap == nil {
-		s.Memtag_heap = boolPtr(true)
-		s.Diag.Memtag_heap = boolPtr(true)
+	if ctx.testBinary() {
+		if s.Memtag_heap == nil {
+			s.Memtag_heap = proptools.BoolPtr(true)
+		}
+		if s.Diag.Memtag_heap == nil {
+			s.Diag.Memtag_heap = proptools.BoolPtr(true)
+		}
 	}
 
 	var globalSanitizers []string
@@ -291,48 +343,48 @@
 	if len(globalSanitizers) > 0 {
 		var found bool
 		if found, globalSanitizers = removeFromList("undefined", globalSanitizers); found && s.All_undefined == nil {
-			s.All_undefined = boolPtr(true)
+			s.All_undefined = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("default-ub", globalSanitizers); found && s.Undefined == nil {
-			s.Undefined = boolPtr(true)
+			s.Undefined = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("address", globalSanitizers); found && s.Address == nil {
-			s.Address = boolPtr(true)
+			s.Address = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("thread", globalSanitizers); found && s.Thread == nil {
-			s.Thread = boolPtr(true)
+			s.Thread = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("fuzzer", globalSanitizers); found && s.Fuzzer == nil {
-			s.Fuzzer = boolPtr(true)
+			s.Fuzzer = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("safe-stack", globalSanitizers); found && s.Safestack == nil {
-			s.Safestack = boolPtr(true)
+			s.Safestack = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("cfi", globalSanitizers); found && s.Cfi == nil {
 			if !ctx.Config().CFIDisabledForPath(ctx.ModuleDir()) {
-				s.Cfi = boolPtr(true)
+				s.Cfi = proptools.BoolPtr(true)
 			}
 		}
 
 		// Global integer_overflow builds do not support static libraries.
 		if found, globalSanitizers = removeFromList("integer_overflow", globalSanitizers); found && s.Integer_overflow == nil {
 			if !ctx.Config().IntegerOverflowDisabledForPath(ctx.ModuleDir()) && !ctx.static() {
-				s.Integer_overflow = boolPtr(true)
+				s.Integer_overflow = proptools.BoolPtr(true)
 			}
 		}
 
 		if found, globalSanitizers = removeFromList("scudo", globalSanitizers); found && s.Scudo == nil {
-			s.Scudo = boolPtr(true)
+			s.Scudo = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("hwaddress", globalSanitizers); found && s.Hwaddress == nil {
-			s.Hwaddress = boolPtr(true)
+			s.Hwaddress = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("writeonly", globalSanitizers); found && s.Writeonly == nil {
@@ -341,11 +393,11 @@
 			if s.Address == nil && s.Hwaddress == nil {
 				ctx.ModuleErrorf("writeonly modifier cannot be used without 'address' or 'hwaddress'")
 			}
-			s.Writeonly = boolPtr(true)
+			s.Writeonly = proptools.BoolPtr(true)
 		}
 		if found, globalSanitizers = removeFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil {
 			if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
-				s.Memtag_heap = boolPtr(true)
+				s.Memtag_heap = proptools.BoolPtr(true)
 			}
 		}
 
@@ -356,17 +408,17 @@
 		// Global integer_overflow builds do not support static library diagnostics.
 		if found, globalSanitizersDiag = removeFromList("integer_overflow", globalSanitizersDiag); found &&
 			s.Diag.Integer_overflow == nil && Bool(s.Integer_overflow) && !ctx.static() {
-			s.Diag.Integer_overflow = boolPtr(true)
+			s.Diag.Integer_overflow = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizersDiag = removeFromList("cfi", globalSanitizersDiag); found &&
 			s.Diag.Cfi == nil && Bool(s.Cfi) {
-			s.Diag.Cfi = boolPtr(true)
+			s.Diag.Cfi = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizersDiag = removeFromList("memtag_heap", globalSanitizersDiag); found &&
 			s.Diag.Memtag_heap == nil && Bool(s.Memtag_heap) {
-			s.Diag.Memtag_heap = boolPtr(true)
+			s.Diag.Memtag_heap = proptools.BoolPtr(true)
 		}
 
 		if len(globalSanitizersDiag) > 0 {
@@ -378,30 +430,30 @@
 	if ctx.Arch().ArchType == android.Arm64 {
 		if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
 			if s.Memtag_heap == nil {
-				s.Memtag_heap = boolPtr(true)
+				s.Memtag_heap = proptools.BoolPtr(true)
 			}
 			if s.Diag.Memtag_heap == nil {
-				s.Diag.Memtag_heap = boolPtr(true)
+				s.Diag.Memtag_heap = proptools.BoolPtr(true)
 			}
 		} else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) {
 			if s.Memtag_heap == nil {
-				s.Memtag_heap = boolPtr(true)
+				s.Memtag_heap = proptools.BoolPtr(true)
 			}
 		}
 	}
 
 	// Enable CFI for all components in the include paths (for Aarch64 only)
 	if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
-		s.Cfi = boolPtr(true)
+		s.Cfi = proptools.BoolPtr(true)
 		if inList("cfi", ctx.Config().SanitizeDeviceDiag()) {
-			s.Diag.Cfi = boolPtr(true)
+			s.Diag.Cfi = proptools.BoolPtr(true)
 		}
 	}
 
 	// Is CFI actually enabled?
 	if !ctx.Config().EnableCFI() {
-		s.Cfi = boolPtr(false)
-		s.Diag.Cfi = boolPtr(false)
+		s.Cfi = nil
+		s.Diag.Cfi = nil
 	}
 
 	// HWASan requires AArch64 hardware feature (top-byte-ignore).
@@ -421,14 +473,14 @@
 
 	// Also disable CFI if ASAN is enabled.
 	if Bool(s.Address) || Bool(s.Hwaddress) {
-		s.Cfi = boolPtr(false)
-		s.Diag.Cfi = boolPtr(false)
+		s.Cfi = nil
+		s.Diag.Cfi = nil
 	}
 
 	// Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
 	if !ctx.Os().Linux() {
-		s.Cfi = boolPtr(false)
-		s.Diag.Cfi = boolPtr(false)
+		s.Cfi = nil
+		s.Diag.Cfi = nil
 		s.Misc_undefined = nil
 		s.Undefined = nil
 		s.All_undefined = nil
@@ -443,8 +495,8 @@
 			s.Cfi = nil
 			s.Diag.Cfi = nil
 		} else {
-			s.Cfi = boolPtr(false)
-			s.Diag.Cfi = boolPtr(false)
+			s.Cfi = nil
+			s.Diag.Cfi = nil
 		}
 	}
 
@@ -495,18 +547,10 @@
 	// TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is
 	// mutually incompatible.
 	if Bool(s.Fuzzer) {
-		s.Cfi = boolPtr(false)
+		s.Cfi = nil
 	}
 }
 
-func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps {
-	if !sanitize.Properties.SanitizerEnabled { // || c.static() {
-		return deps
-	}
-
-	return deps
-}
-
 func toDisableImplicitIntegerChange(flags []string) bool {
 	// Returns true if any flag is fsanitize*integer, and there is
 	// no explicit flag about sanitize=implicit-integer-sign-change.
@@ -789,23 +833,27 @@
 }
 
 func (sanitize *sanitize) SetSanitizer(t SanitizerType, b bool) {
+	bPtr := proptools.BoolPtr(b)
+	if !b {
+		bPtr = nil
+	}
 	switch t {
 	case Asan:
-		sanitize.Properties.Sanitize.Address = boolPtr(b)
+		sanitize.Properties.Sanitize.Address = bPtr
 	case Hwasan:
-		sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
+		sanitize.Properties.Sanitize.Hwaddress = bPtr
 	case tsan:
-		sanitize.Properties.Sanitize.Thread = boolPtr(b)
+		sanitize.Properties.Sanitize.Thread = bPtr
 	case intOverflow:
-		sanitize.Properties.Sanitize.Integer_overflow = boolPtr(b)
+		sanitize.Properties.Sanitize.Integer_overflow = bPtr
 	case cfi:
-		sanitize.Properties.Sanitize.Cfi = boolPtr(b)
+		sanitize.Properties.Sanitize.Cfi = bPtr
 	case scs:
-		sanitize.Properties.Sanitize.Scs = boolPtr(b)
+		sanitize.Properties.Sanitize.Scs = bPtr
 	case memtag_heap:
-		sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b)
+		sanitize.Properties.Sanitize.Memtag_heap = bPtr
 	case Fuzzer:
-		sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
+		sanitize.Properties.Sanitize.Fuzzer = bPtr
 	default:
 		panic(fmt.Errorf("unknown SanitizerType %d", t))
 	}
@@ -1261,7 +1309,7 @@
 func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) {
 	return func(mctx android.BottomUpMutatorContext) {
 		if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
-			if c.IsDependencyRoot() && c.IsSanitizerEnabled(t) {
+			if c.Binary() && c.IsSanitizerEnabled(t) {
 				modules := mctx.CreateVariations(t.variationName())
 				modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
 			} else if c.IsSanitizerEnabled(t) || c.SanitizeDep() {
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 4430fc3..0070e40 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -222,11 +222,12 @@
 	case Async:
 		return "async"
 	default:
-		panic("invalid note type")
+		panic("type_note_invalid")
 	}
 }
 
 func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
+	t.Helper()
 	note_async := "note_memtag_heap_async"
 	note_sync := "note_memtag_heap_sync"
 
@@ -251,75 +252,86 @@
 	android.FixtureModifyMockFS(func(fs android.MockFS) {
 		templateBp := `
 		cc_test {
-			name: "%[1]s_test",
+			name: "unset_test_%[1]s",
 			gtest: false,
 		}
 
 		cc_test {
-			name: "%[1]s_test_false",
+			name: "no_memtag_test_%[1]s",
 			gtest: false,
 			sanitize: { memtag_heap: false },
 		}
 
 		cc_test {
-			name: "%[1]s_test_true",
+			name: "set_memtag_test_%[1]s",
 			gtest: false,
 			sanitize: { memtag_heap: true },
 		}
 
 		cc_test {
-			name: "%[1]s_test_true_nodiag",
+			name: "set_memtag_set_async_test_%[1]s",
 			gtest: false,
 			sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
 		}
 
 		cc_test {
-			name: "%[1]s_test_true_diag",
+			name: "set_memtag_set_sync_test_%[1]s",
 			gtest: false,
 			sanitize: { memtag_heap: true, diag: { memtag_heap: true }  },
 		}
 
-		cc_binary {
-			name: "%[1]s_binary",
+		cc_test {
+			name: "unset_memtag_set_sync_test_%[1]s",
+			gtest: false,
+			sanitize: { diag: { memtag_heap: true }  },
 		}
 
 		cc_binary {
-			name: "%[1]s_binary_false",
+			name: "unset_binary_%[1]s",
+		}
+
+		cc_binary {
+			name: "no_memtag_binary_%[1]s",
 			sanitize: { memtag_heap: false },
 		}
 
 		cc_binary {
-			name: "%[1]s_binary_true",
+			name: "set_memtag_binary_%[1]s",
 			sanitize: { memtag_heap: true },
 		}
 
 		cc_binary {
-			name: "%[1]s_binary_true_nodiag",
+			name: "set_memtag_set_async_binary_%[1]s",
 			sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
 		}
 
 		cc_binary {
-			name: "%[1]s_binary_true_diag",
+			name: "set_memtag_set_sync_binary_%[1]s",
 			sanitize: { memtag_heap: true, diag: { memtag_heap: true }  },
 		}
+
+		cc_binary {
+			name: "unset_memtag_set_sync_binary_%[1]s",
+			sanitize: { diag: { memtag_heap: true }  },
+		}
 		`
-		subdirDefaultBp := fmt.Sprintf(templateBp, "default")
-		subdirExcludeBp := fmt.Sprintf(templateBp, "exclude")
-		subdirSyncBp := fmt.Sprintf(templateBp, "sync")
-		subdirAsyncBp := fmt.Sprintf(templateBp, "async")
+		subdirNoOverrideBp := fmt.Sprintf(templateBp, "no_override")
+		subdirOverrideDefaultDisableBp := fmt.Sprintf(templateBp, "override_default_disable")
+		subdirSyncBp := fmt.Sprintf(templateBp, "override_default_sync")
+		subdirAsyncBp := fmt.Sprintf(templateBp, "override_default_async")
 
 		fs.Merge(android.MockFS{
-			"subdir_default/Android.bp": []byte(subdirDefaultBp),
-			"subdir_exclude/Android.bp": []byte(subdirExcludeBp),
-			"subdir_sync/Android.bp":    []byte(subdirSyncBp),
-			"subdir_async/Android.bp":   []byte(subdirAsyncBp),
+			"subdir_no_override/Android.bp":              []byte(subdirNoOverrideBp),
+			"subdir_override_default_disable/Android.bp": []byte(subdirOverrideDefaultDisableBp),
+			"subdir_sync/Android.bp":                     []byte(subdirSyncBp),
+			"subdir_async/Android.bp":                    []byte(subdirAsyncBp),
 		})
 	}),
 	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-		variables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
-		// "subdir_exclude" is covered by both include and exclude paths. Exclude wins.
-		variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_exclude"}
-		variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_exclude"}
+		variables.MemtagHeapExcludePaths = []string{"subdir_override_default_disable"}
+		// "subdir_override_default_disable" is covered by both include and override_default_disable paths. override_default_disable wins.
+		variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_override_default_disable"}
+		variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_override_default_disable"}
 	}),
 )
 
@@ -332,53 +344,67 @@
 	).RunTest(t)
 	ctx := result.TestContext
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
 }
 
 func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
@@ -393,53 +419,66 @@
 	).RunTest(t)
 	ctx := result.TestContext
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
 }
 
 func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
@@ -455,51 +494,64 @@
 	).RunTest(t)
 	ctx := result.TestContext
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
 }
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 3a382a1..4f031ff 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -264,6 +264,7 @@
 func init() {
 	VendorSnapshotImageSingleton.Init(android.InitRegistrationContext)
 	recoverySnapshotImageSingleton.init(android.InitRegistrationContext)
+	android.RegisterMakeVarsProvider(pctx, snapshotMakeVarsProvider)
 }
 
 const (
@@ -383,6 +384,24 @@
 
 var _ android.ImageInterface = (*snapshot)(nil)
 
+func snapshotMakeVarsProvider(ctx android.MakeVarsContext) {
+	snapshotSet := map[string]struct{}{}
+	ctx.VisitAllModules(func(m android.Module) {
+		if s, ok := m.(*snapshot); 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.
+				return
+			}
+			snapshotSet[s.Name()] = struct{}{}
+			imageNameVersion := strings.Split(s.image.imageVariantName(ctx.DeviceConfig()), ".")
+			ctx.Strict(
+				strings.Join([]string{strings.ToUpper(imageNameVersion[0]), s.baseSnapshot.Version(), "SNAPSHOT_DIR"}, "_"),
+				ctx.ModuleDir(s))
+		}
+	})
+}
+
 func vendorSnapshotFactory() android.Module {
 	return snapshotFactory(VendorSnapshotImageSingleton)
 }
@@ -931,7 +950,7 @@
 // development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_object
 // overrides the vendor variant of the cc object with the same name, if BOARD_VNDK_VERSION is set.
 func VendorSnapshotObjectFactory() android.Module {
-	module := newObject()
+	module := newObject(android.DeviceSupported)
 
 	prebuilt := &snapshotObjectLinker{
 		objectLinker: objectLinker{
@@ -949,7 +968,7 @@
 // development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_object
 // overrides the recovery variant of the cc object with the same name, if BOARD_VNDK_VERSION is set.
 func RecoverySnapshotObjectFactory() android.Module {
-	module := newObject()
+	module := newObject(android.DeviceSupported)
 
 	prebuilt := &snapshotObjectLinker{
 		objectLinker: objectLinker{
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index a6c8ed5..b0538be 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -53,6 +53,10 @@
 	return m.Properties.SnapshotSharedLibs
 }
 
+func (m *Module) SnapshotStaticLibs() []string {
+	return m.Properties.SnapshotStaticLibs
+}
+
 // snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots.
 type snapshotLibraryInterface interface {
 	libraryInterface
diff --git a/cc/testing.go b/cc/testing.go
index 80cc0ef..b9d84f6 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -367,6 +367,7 @@
 			stl: "none",
 			min_sdk_version: "16",
 			crt: true,
+			default_shared_libs: [],
 			apex_available: [
 				"//apex_available:platform",
 				"//apex_available:anyapex",
diff --git a/cc/tidy.go b/cc/tidy.go
index 616cf8a..b2382e8 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -61,13 +61,6 @@
 	return []interface{}{&tidy.Properties}
 }
 
-func (tidy *tidyFeature) begin(ctx BaseModuleContext) {
-}
-
-func (tidy *tidyFeature) deps(ctx DepsContext, deps Deps) Deps {
-	return deps
-}
-
 func (tidy *tidyFeature) flags(ctx ModuleContext, flags Flags) Flags {
 	CheckBadTidyFlags(ctx, "tidy_flags", tidy.Properties.Tidy_flags)
 	CheckBadTidyChecks(ctx, "tidy_checks", tidy.Properties.Tidy_checks)
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 5a303c8..003b7c9 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -242,10 +242,12 @@
 	SanitizeUbsanDep   bool     `json:",omitempty"`
 
 	// binary flags
-	Symlinks []string `json:",omitempty"`
+	Symlinks         []string `json:",omitempty"`
+	StaticExecutable bool     `json:",omitempty"`
 
 	// dependencies
 	SharedLibs  []string `json:",omitempty"`
+	StaticLibs  []string `json:",omitempty"`
 	RuntimeLibs []string `json:",omitempty"`
 	Required    []string `json:",omitempty"`
 
@@ -381,6 +383,8 @@
 			if m.Shared() {
 				prop.SharedLibs = m.SnapshotSharedLibs()
 			}
+			// static libs dependencies are required to collect the NOTICE files.
+			prop.StaticLibs = m.SnapshotStaticLibs()
 			if sanitizable, ok := m.(PlatformSanitizeable); ok {
 				if sanitizable.Static() && sanitizable.SanitizePropDefined() {
 					prop.SanitizeMinimalDep = sanitizable.MinimalRuntimeDep() || sanitizable.MinimalRuntimeNeeded()
@@ -426,8 +430,10 @@
 		} else if m.Binary() {
 			// binary flags
 			prop.Symlinks = m.Symlinks()
+			prop.StaticExecutable = m.StaticExecutable()
 			prop.SharedLibs = m.SnapshotSharedLibs()
-
+			// static libs dependencies are required to collect the NOTICE files.
+			prop.StaticLibs = m.SnapshotStaticLibs()
 			// install bin
 			binPath := m.OutputFile().Path()
 			snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
@@ -495,13 +501,13 @@
 			headers = append(headers, m.SnapshotHeaders()...)
 		}
 
-		if len(m.NoticeFiles()) > 0 {
+		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
-				snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.NoticeFiles(), noticeOut))
+				snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.EffectiveLicenseFiles(), noticeOut))
 			}
 		}
 	})
diff --git a/cc/vndk.go b/cc/vndk.go
index 0b40076..499d428 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -100,12 +100,6 @@
 	return []interface{}{&vndk.Properties}
 }
 
-func (vndk *vndkdep) begin(ctx BaseModuleContext) {}
-
-func (vndk *vndkdep) deps(ctx BaseModuleContext, deps Deps) Deps {
-	return deps
-}
-
 func (vndk *vndkdep) isVndk() bool {
 	return Bool(vndk.Properties.Vndk.Enabled)
 }
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 57b4c4f..94b9adf 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -15,6 +15,7 @@
 package dexpreopt
 
 import (
+	"encoding/json"
 	"fmt"
 	"sort"
 	"strconv"
@@ -363,6 +364,15 @@
 	return ulibs
 }
 
+func (clcMap ClassLoaderContextMap) Dump() string {
+	jsonCLC := toJsonClassLoaderContext(clcMap)
+	bytes, err := json.MarshalIndent(jsonCLC, "", "  ")
+	if err != nil {
+		panic(err)
+	}
+	return string(bytes)
+}
+
 // Now that the full unconditional context is known, reconstruct conditional context.
 // Apply filters for individual libraries, mirroring what the PackageManager does when it
 // constructs class loader context on device.
diff --git a/java/app.go b/java/app.go
index fc1ace0..fc6e183 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1213,7 +1213,7 @@
 }
 
 func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) {
-	if !ctx.Config().UnbundledBuild() {
+	if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() {
 		ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...)
 		ctx.AddVariationDependencies(nil, usesLibTag, u.presentOptionalUsesLibs(ctx)...)
 		// Only add these extra dependencies if the module depends on framework libs. This avoids
@@ -1249,15 +1249,16 @@
 // to their dex jars on host and on device.
 func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap {
 	clcMap := make(dexpreopt.ClassLoaderContextMap)
-
-	if !ctx.Config().UnbundledBuild() {
+	// Skip when UnbundledBuild() is true, but UnbundledBuildImage() is false.
+	// Added UnbundledBuildImage() condition to generate dexpreopt.config even though unbundled image is built.
+	if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() {
 		ctx.VisitDirectDeps(func(m android.Module) {
 			if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
 				dep := ctx.OtherModuleName(m)
 				if lib, ok := m.(UsesLibraryDependency); ok {
-					libName := dep
+					libName := android.RemoveOptionalPrebuiltPrefix(dep)
 					if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
-						libName = *ulib.ProvidesUsesLib()
+						libName = android.RemoveOptionalPrebuiltPrefix(*ulib.ProvidesUsesLib())
 						// Replace module name with library name in `uses_libs`/`optional_uses_libs`
 						// in order to pass verify_uses_libraries check (which compares these
 						// properties against library names written in the manifest).
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 515dd89..bdf0dae 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -162,11 +162,12 @@
 	// versioned sdk.
 	produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput
 
-	// produceBootImageFiles produces the boot image (i.e. .art, .oat and .vdex) files for each of the
-	// required android.ArchType values in the returned map.
+	// produceBootImageFiles will attempt to produce rules to create the boot image files at the paths
+	// predefined in the bootImageConfig.
 	//
-	// It must return nil if the boot image files cannot be produced for whatever reason.
-	produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch
+	// If it could not create the files then it will return nil. Otherwise, it will return a map from
+	// android.ArchType to the predefined paths of the boot image files.
+	produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch
 }
 
 var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
@@ -454,9 +455,13 @@
 		if imageConfig != nil {
 			// Delegate the production of the boot image files to a module type specific method.
 			common := ctx.Module().(commonBootclasspathFragment)
-			bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig, contents)
+			bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig)
 
 			if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
+				// Zip the boot image files up, if available. This will generate the zip file in a
+				// predefined location.
+				buildBootImageZipInPredefinedLocation(ctx, imageConfig, bootImageFilesByArch)
+
 				// Copy the dex jars of this fragment's content modules to their predefined locations.
 				copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
 			}
@@ -532,27 +537,18 @@
 
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	// Convert content names to their appropriate stems, in case a test library is overriding an actual boot jar
-	var stems []string
-	for _, name := range b.properties.Contents {
-		dep := ctx.GetDirectDepWithTag(name, bootclasspathFragmentContentDepTag)
-		if m, ok := dep.(ModuleWithStem); ok {
-			stems = append(stems, m.Stem())
-		} else {
-			ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name)
-		}
-	}
+	possibleUpdatableModules := gatherPossibleUpdatableModuleNamesAndStems(ctx, b.properties.Contents, bootclasspathFragmentContentDepTag)
 
 	// Only create configs for updatable boot jars. Non-updatable boot jars must be part of the
 	// platform_bootclasspath's classpath proto config to guarantee that they come before any
 	// updatable jars at runtime.
-	jars := global.UpdatableBootJars.Filter(stems)
+	jars := global.UpdatableBootJars.Filter(possibleUpdatableModules)
 
 	// TODO(satayev): for apex_test we want to include all contents unconditionally to classpaths
 	// config. However, any test specific jars would not be present in UpdatableBootJars. Instead,
 	// we should check if we are creating a config for apex_test via ApexInfo and amend the values.
 	// This is an exception to support end-to-end test for SdkExtensions, until such support exists.
-	if android.InList("test_framework-sdkextensions", stems) {
+	if android.InList("test_framework-sdkextensions", possibleUpdatableModules) {
 		jars = jars.Append("com.android.sdkext", "test_framework-sdkextensions")
 	}
 	return jars
@@ -673,55 +669,49 @@
 }
 
 // produceBootImageFiles builds the boot image files from the source if it is required.
-func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
 	if SkipDexpreoptBootJars(ctx) {
 		return nil
 	}
 
-	// Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
-	// GenerateSingletonBuildActions method as it cannot create it for itself.
-	dexpreopt.GetGlobalSoongConfig(ctx)
-
 	// Only generate the boot image if the configuration does not skip it.
-	if !b.generateBootImageBuildActions(ctx, contents, imageConfig) {
-		return nil
-	}
-
-	// Only make the files available to an apex if they were actually generated.
-	files := bootImageFilesByArch{}
-	for _, variant := range imageConfig.apexVariants() {
-		files[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
-	}
-
-	return files
+	return b.generateBootImageBuildActions(ctx, imageConfig)
 }
 
 // generateBootImageBuildActions generates ninja rules to create the boot image if required for this
 // module.
 //
-// Returns true if the boot image is created, false otherwise.
-func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.ModuleContext, contents []android.Module, imageConfig *bootImageConfig) bool {
+// If it could not create the files then it will return nil. Otherwise, it will return a map from
+// android.ArchType to the predefined paths of the boot image files.
+func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
 	global := dexpreopt.GetGlobalConfig(ctx)
 	if !shouldBuildBootImages(ctx.Config(), global) {
-		return false
+		return nil
 	}
 
 	// Bootclasspath fragment modules that are for the platform do not produce a boot image.
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if apexInfo.IsForPlatform() {
-		return false
+		return nil
 	}
 
 	// Bootclasspath fragment modules that are versioned do not produce a boot image.
 	if android.IsModuleInVersionedSdk(ctx.Module()) {
-		return false
+		return nil
 	}
 
 	// Build a profile for the image config and then use that to build the boot image.
 	profile := bootImageProfileRule(ctx, imageConfig)
-	buildBootImage(ctx, imageConfig, profile)
 
-	return true
+	// Build boot image files for the host variants.
+	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
+
+	// Build boot image files for the android variants.
+	androidBootImageFilesByArch := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+
+	// Return the boot image files for the android variants for inclusion in an APEX and to be zipped
+	// up for the dist.
+	return androidBootImageFilesByArch
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
@@ -949,7 +939,7 @@
 }
 
 // produceBootImageFiles extracts the boot image files from the APEX if available.
-func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
 	if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
 		return nil
 	}
@@ -971,6 +961,7 @@
 	}
 
 	di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+	files := bootImageFilesByArch{}
 	for _, variant := range imageConfig.apexVariants() {
 		arch := variant.target.Arch.ArchType
 		for _, toPath := range variant.imagesDeps {
@@ -978,6 +969,10 @@
 			// Get the path to the file that the deapexer extracted from the prebuilt apex file.
 			fromPath := di.PrebuiltExportPath(apexRelativePath)
 
+			// Return the toPath as the calling code expects the paths in the returned map to be the
+			// paths predefined in the bootImageConfig.
+			files[arch] = append(files[arch], toPath)
+
 			// Copy the file to the predefined location.
 			ctx.Build(pctx, android.BuildParams{
 				Rule:   android.Cp,
@@ -987,10 +982,13 @@
 		}
 	}
 
-	// The returned files will be made available to APEXes that include a bootclasspath_fragment.
-	// However, as a prebuilt_bootclasspath_fragment can never contribute to an APEX there is no point
-	// in returning any files.
-	return nil
+	// Build the boot image files for the host variants. These are built from the dex files provided
+	// by the contents of this module as prebuilt versions of the host boot image files are not
+	// available, i.e. there is no host specific prebuilt apex containing them. This has to be built
+	// without a profile as the prebuilt modules do not provide a profile.
+	buildBootImageVariantsForBuildOs(ctx, imageConfig, nil)
+
+	return files
 }
 
 var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil)
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index f7a200a..12bb711 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -91,6 +91,29 @@
 	maxSdkVersion int32
 }
 
+// gatherPossibleUpdatableModuleNamesAndStems returns a set of module and stem names from the
+// supplied contents that may be in the updatable boot jars.
+//
+// The module names are included because sometimes the stem is set to just change the name of
+// the installed file and it expects the configuration to still use the actual module name.
+//
+// The stem names are included because sometimes the stem is set to change the effective name of the
+// module that is used in the configuration as well,e .g. when a test library is overriding an
+// actual boot jar
+func gatherPossibleUpdatableModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string {
+	set := map[string]struct{}{}
+	for _, name := range contents {
+		dep := ctx.GetDirectDepWithTag(name, tag)
+		set[name] = struct{}{}
+		if m, ok := dep.(ModuleWithStem); ok {
+			set[m.Stem()] = struct{}{}
+		} else {
+			ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name)
+		}
+	}
+	return android.SortedStringKeys(set)
+}
+
 // Converts android.ConfiguredJarList into a list of classpathJars for each given classpathType.
 func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, classpaths ...classpathType) []classpathJar {
 	paths := configuredJars.DevicePaths(ctx.Config(), android.Android)
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
new file mode 100644
index 0000000..51d998a
--- /dev/null
+++ b/java/core-libraries/Android.bp
@@ -0,0 +1,194 @@
+// Copyright (C) 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.
+
+//
+// Definitions for building the Android core libraries, i.e. ART, I18n and
+// Conscrypt.
+//
+// These are here as the definitions are used by the build itself and include
+// parts from all three of those modules.
+//
+
+// A stubs target containing the parts of the public SDK API provided by the
+// core libraries.
+//
+// Don't use this directly, use "sdk_version: core_current".
+java_library {
+    name: "core.current.stubs",
+    visibility: ["//visibility:public"],
+    static_libs: [
+        "art.module.public.api.stubs",
+        "conscrypt.module.public.api.stubs",
+        "i18n.module.public.api.stubs",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+
+    dist: {
+        targets: [
+            "sdk",
+            "win_sdk",
+        ],
+    },
+}
+
+// Distributed with the SDK for turning into system modules to compile apps
+// against.
+java_library {
+    name: "core-current-stubs-for-system-modules",
+    visibility: ["//development/sdk"],
+    static_libs: [
+        "core.current.stubs",
+        // This one is not on device but it's needed when javac compiles code
+        // containing lambdas.
+        "core-lambda-stubs-for-system-modules",
+        // This one is not on device but it's needed when javac compiles code
+        // containing @Generated annotations produced by some code generation
+        // tools.
+        // See http://b/123891440.
+        "core-generated-annotation-stubs",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+    dist: {
+        dest: "core-for-system-modules.jar",
+        targets: [
+            "sdk",
+            "win_sdk",
+        ],
+    },
+}
+
+// Used when compiling higher-level code against core.current.stubs.
+java_system_modules {
+    name: "core-current-stubs-system-modules",
+    visibility: ["//visibility:public"],
+    libs: [
+        "core-current-stubs-for-system-modules",
+    ],
+}
+
+// A stubs target containing the parts of the public SDK & @SystemApi(MODULE_LIBRARIES) API
+// provided by the core libraries.
+//
+// Don't use this directly, use "sdk_version: module_current".
+java_library {
+    name: "core.module_lib.stubs",
+    static_libs: [
+        "art.module.public.api.stubs.module_lib",
+
+        // Replace the following with the module-lib correspondence when Conscrypt or i18N module
+        // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides
+        // @SystemApi(MODULE_LIBRARIES).
+        "conscrypt.module.public.api.stubs",
+        "i18n.module.public.api.stubs",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+    visibility: ["//visibility:private"],
+}
+
+// Used when compiling higher-level code with sdk_version "module_current"
+java_system_modules {
+    name: "core-module-lib-stubs-system-modules",
+    libs: [
+        "core.module_lib.stubs",
+        // This one is not on device but it's needed when javac compiles code
+        // containing lambdas.
+        "core-lambda-stubs-for-system-modules",
+        // This one is not on device but it's needed when javac compiles code
+        // containing @Generated annotations produced by some code generation
+        // tools.
+        // See http://b/123891440.
+        "core-generated-annotation-stubs",
+    ],
+    visibility: ["//visibility:public"],
+}
+
+// Ideally this should be a restricted allowlist but there are hundreds of modules that depend on
+// this.
+// TODO(http://b/134561230) - limit the number of dependents on this.
+core_platform_visibility = ["//visibility:public"]
+
+// Libraries containing the core platform API stubs for the core libraries.
+//
+// Although this stubs library is primarily used by the Java compiler / build to indicate
+// the core platform API surface area, compile_dex: true is used so that the Core Platform
+// API annotations are available to the dex tools that enable enforcement of runtime
+// accessibility. b/119068555
+java_library {
+    name: "legacy.core.platform.api.stubs",
+    visibility: core_platform_visibility,
+    hostdex: true,
+    compile_dex: true,
+
+    sdk_version: "none",
+    system_modules: "none",
+    static_libs: [
+        "art.module.public.api.stubs.module_lib",
+        "conscrypt.module.platform.api.stubs",
+        "legacy.i18n.module.platform.api.stubs",
+    ],
+    patch_module: "java.base",
+}
+
+java_library {
+    name: "stable.core.platform.api.stubs",
+    visibility: core_platform_visibility,
+    hostdex: true,
+    compile_dex: true,
+
+    sdk_version: "none",
+    system_modules: "none",
+    static_libs: [
+        "art.module.public.api.stubs.module_lib",
+        // conscrypt only has a stable version, so it is okay to depend on it here:
+        "conscrypt.module.platform.api.stubs",
+        "stable.i18n.module.platform.api.stubs",
+    ],
+    patch_module: "java.base",
+}
+
+// Used when compiling higher-level code against *.core.platform.api.stubs.
+java_system_modules {
+    name: "legacy-core-platform-api-stubs-system-modules",
+    visibility: core_platform_visibility,
+    libs: [
+        "legacy.core.platform.api.stubs",
+        // This one is not on device but it's needed when javac compiles code
+        // containing lambdas.
+        "core-lambda-stubs-for-system-modules",
+        // This one is not on device but it's needed when javac compiles code
+        // containing @Generated annotations produced by some code generation
+        // tools.
+        // See http://b/123891440.
+        "core-generated-annotation-stubs",
+    ],
+}
+
+java_system_modules {
+    name: "stable-core-platform-api-stubs-system-modules",
+    visibility: core_platform_visibility,
+    libs: [
+        "stable.core.platform.api.stubs",
+        // This one is not on device but it's needed when javac compiles code
+        // containing lambdas.
+        "core-lambda-stubs-for-system-modules",
+        // This one is not on device but it's needed when javac compiles code
+        // containing @Generated annotations produced by some code generation
+        // tools.
+        // See http://b/123891440.
+        "core-generated-annotation-stubs",
+    ],
+}
diff --git a/java/core-libraries/OWNERS b/java/core-libraries/OWNERS
new file mode 100644
index 0000000..bb3546a
--- /dev/null
+++ b/java/core-libraries/OWNERS
@@ -0,0 +1,3 @@
+include platform/external/icu:/OWNERS
+include platform/external/conscrypt:/OWNERS
+include platform/libcore:/OWNERS
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 2e46d74..0faae36 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -141,10 +141,9 @@
 		}
 	}
 
-	// If it is neither app nor test, make config files regardless of its dexpreopt setting.
+	// If it is test, make config files regardless of its dexpreopt setting.
 	// The config files are required for apps defined in make which depend on the lib.
-	// TODO(b/158843648): The config for apps should be generated as well regardless of setting.
-	if (d.isApp || d.isTest) && d.dexpreoptDisabled(ctx) {
+	if d.isTest && d.dexpreoptDisabled(ctx) {
 		return
 	}
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 03769fa..19c65ca 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -502,32 +502,72 @@
 	}
 }
 
-// buildBootImage takes a bootImageConfig, and creates rules to build it.
-func buildBootImage(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) {
-	var zipFiles android.Paths
+// buildBootImageVariantsForAndroidOs generates rules to build the boot image variants for the
+// android.Android OsType and returns a map from the architectures to the paths of the generated
+// boot image files.
+//
+// The paths are returned because they are needed elsewhere in Soong, e.g. for populating an APEX.
+func buildBootImageVariantsForAndroidOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) bootImageFilesByArch {
+	return buildBootImageForOsType(ctx, image, profile, android.Android)
+}
+
+// buildBootImageVariantsForBuildOs generates rules to build the boot image variants for the
+// android.BuildOs OsType, i.e. the type of OS on which the build is being running.
+//
+// The files need to be generated into their predefined location because they are used from there
+// both within Soong and outside, e.g. for ART based host side testing and also for use by some
+// cloud based tools. However, they are not needed by callers of this function and so the paths do
+// not need to be returned from this func, unlike the buildBootImageVariantsForAndroidOs func.
+func buildBootImageVariantsForBuildOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) {
+	buildBootImageForOsType(ctx, image, profile, android.BuildOs)
+}
+
+// buildBootImageForOsType takes a bootImageConfig, a profile file and an android.OsType
+// boot image files are required for and it creates rules to build the boot image
+// files for all the required architectures for them.
+//
+// It returns a map from android.ArchType to the predefined paths of the boot image files.
+func buildBootImageForOsType(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath, requiredOsType android.OsType) bootImageFilesByArch {
+	filesByArch := bootImageFilesByArch{}
 	for _, variant := range image.variants {
-		files := buildBootImageVariant(ctx, variant, profile)
-		if variant.target.Os == android.Android {
-			zipFiles = append(zipFiles, files.Paths()...)
+		if variant.target.Os == requiredOsType {
+			buildBootImageVariant(ctx, variant, profile)
+			filesByArch[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
 		}
 	}
 
-	if image.zip != nil {
-		rule := android.NewRuleBuilder(pctx, ctx)
-		rule.Command().
-			BuiltTool("soong_zip").
-			FlagWithOutput("-o ", image.zip).
-			FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()).
-			FlagWithInputList("-f ", zipFiles, " -f ")
+	return filesByArch
+}
 
-		rule.Build("zip_"+image.name, "zip "+image.name+" image")
+// buildBootImageZipInPredefinedLocation generates a zip file containing all the boot image files.
+//
+// The supplied filesByArch is nil when the boot image files have not been generated. Otherwise, it
+// is a map from android.ArchType to the predefined locations.
+func buildBootImageZipInPredefinedLocation(ctx android.ModuleContext, image *bootImageConfig, filesByArch bootImageFilesByArch) {
+	if filesByArch == nil {
+		return
 	}
+
+	// Compute the list of files from all the architectures.
+	zipFiles := android.Paths{}
+	for _, archType := range android.ArchTypeList() {
+		zipFiles = append(zipFiles, filesByArch[archType]...)
+	}
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().
+		BuiltTool("soong_zip").
+		FlagWithOutput("-o ", image.zip).
+		FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()).
+		FlagWithInputList("-f ", zipFiles, " -f ")
+
+	rule.Build("zip_"+image.name, "zip "+image.name+" image")
 }
 
 // Generate boot image build rules for a specific target.
-func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) android.WritablePaths {
+func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) {
 
-	globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
+	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
 	arch := image.target.Arch.ArchType
@@ -641,11 +681,8 @@
 	var vdexInstalls android.RuleBuilderInstalls
 	var unstrippedInstalls android.RuleBuilderInstalls
 
-	var zipFiles android.WritablePaths
-
 	for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
 		cmd.ImplicitOutput(artOrOat)
-		zipFiles = append(zipFiles, artOrOat)
 
 		// Install the .oat and .art files
 		rule.Install(artOrOat, filepath.Join(installDir, artOrOat.Base()))
@@ -653,7 +690,6 @@
 
 	for _, vdex := range image.moduleFiles(ctx, outputDir, ".vdex") {
 		cmd.ImplicitOutput(vdex)
-		zipFiles = append(zipFiles, vdex)
 
 		// Note that the vdex files are identical between architectures.
 		// Make rules will create symlinks to share them between architectures.
@@ -675,8 +711,6 @@
 	image.installs = rule.Installs()
 	image.vdexInstalls = vdexInstalls
 	image.unstrippedInstalls = unstrippedInstalls
-
-	return zipFiles
 }
 
 const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
@@ -684,7 +718,7 @@
 Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
 
 func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath {
-	globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
+	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
 	if global.DisableGenerateProfile {
diff --git a/java/droidstubs.go b/java/droidstubs.go
index d348b55..ec1b04a 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -128,12 +128,15 @@
 	// whicih can be used for scheduling purposes
 	High_mem *bool
 
-	// is set to true, Metalava will allow framework SDK to contain API levels annotations.
+	// if set to true, Metalava will allow framework SDK to contain API levels annotations.
 	Api_levels_annotations_enabled *bool
 
 	// the dirs which Metalava extracts API levels annotations from.
 	Api_levels_annotations_dirs []string
 
+	// the sdk kind which Metalava extracts API levels annotations from. Supports 'public' and 'system' for now; defaults to public.
+	Api_levels_sdk_type *string
+
 	// the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
 	Api_levels_jar_filename *string
 
@@ -367,6 +370,7 @@
 
 	filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
 
+	var dirs []string
 	ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
 		if t, ok := m.(*ExportedDroiddocDir); ok {
 			for _, dep := range t.deps {
@@ -383,12 +387,32 @@
 					cmd.Implicit(dep)
 				}
 			}
-			cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/"+filename)
+
+			dirs = append(dirs, t.dir.String())
 		} else {
 			ctx.PropertyErrorf("api_levels_annotations_dirs",
 				"module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
 		}
 	})
+
+	// Add all relevant --android-jar-pattern patterns for Metalava.
+	// When parsing a stub jar for a specific version, Metalava picks the first pattern that defines
+	// an actual file present on disk (in the order the patterns were passed). For system APIs for
+	// privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs
+	// for older releases.
+	if sdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public"); sdkType != "public" {
+		if sdkType != "system" {
+			ctx.PropertyErrorf("api_levels_sdk_type", "only 'public' and 'system' are supported")
+		}
+		// If building non public stubs, add all sdkType patterns first...
+		for _, dir := range dirs {
+			cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkType, filename))
+		}
+	}
+	for _, dir := range dirs {
+		// ... and fallback to public ones, for Metalava to use if needed.
+		cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, "public", filename))
+	}
 }
 
 func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index db664c1..60d0bea 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"reflect"
+	"regexp"
 	"strings"
 	"testing"
 
@@ -81,6 +82,46 @@
 	}
 }
 
+func TestSystemDroidstubs(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		droiddoc_exported_dir {
+			name: "some-exported-dir",
+			path: "somedir",
+		}
+
+		droiddoc_exported_dir {
+			name: "some-other-exported-dir",
+			path: "someotherdir",
+		}
+
+		droidstubs {
+			name: "foo-stubs",
+			srcs: ["foo-doc/a.java"],
+			api_levels_annotations_dirs: [
+				"some-exported-dir",
+				"some-other-exported-dir",
+			],
+			api_levels_annotations_enabled: true,
+            api_levels_sdk_type: "system",
+		}
+		`,
+		map[string][]byte{
+			"foo-doc/a.java": nil,
+		})
+
+	m := ctx.ModuleForTests("foo-stubs", "android_common")
+	manifest := m.Output("metalava.sbox.textproto")
+	cmd := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command)
+	r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`)
+	matches := r.FindAllString(cmd, -1)
+	android.AssertArrayString(t, "order of patterns", []string{
+		"--android-jar-pattern somedir/%/system/android.jar",
+		"--android-jar-pattern someotherdir/%/system/android.jar",
+		"--android-jar-pattern somedir/%/public/android.jar",
+		"--android-jar-pattern someotherdir/%/public/android.jar",
+	}, matches)
+}
+
 func TestDroidstubsSandbox(t *testing.T) {
 	ctx, _ := testJavaWithFS(t, `
 		genrule {
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index af12484..c4832d2 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -1161,6 +1161,14 @@
 		return true
 	}
 
+	// A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there
+	// is no equivalently versioned prebuilt APEX file from which it can be obtained. However,
+	// versioned bootclasspath modules are processed by Soong so in order to avoid them causing build
+	// failures missing boot dex jars need to be deferred.
+	if android.IsModuleInVersionedSdk(ctx.Module()) {
+		return true
+	}
+
 	// This is called for both platform_bootclasspath and bootclasspath_fragment modules.
 	//
 	// A bootclasspath_fragment module should only use the APEX variant of source or prebuilt modules.
@@ -1197,6 +1205,14 @@
 	//
 	// TODO(b/187910671): Remove this once platform variants are no longer created unnecessarily.
 	if android.IsModulePrebuilt(module) {
+		// An inactive source module can still contribute to the APEX but an inactive prebuilt module
+		// should not contribute to anything. So, rather than have a missing dex jar cause a Soong
+		// failure defer the error reporting to Ninja. Unless the prebuilt build target is explicitly
+		// built Ninja should never use the dex jar file.
+		if !isActiveModule(module) {
+			return true
+		}
+
 		if am, ok := module.(android.ApexModule); ok && am.InAnyApex() {
 			apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
 			if apexInfo.IsForPlatform() {
@@ -1205,14 +1221,6 @@
 		}
 	}
 
-	// A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there
-	// is no equivalently versioned prebuilt APEX file from which it can be obtained. However,
-	// versioned bootclasspath modules are processed by Soong so in order to avoid them causing build
-	// failures missing boot dex jars need to be deferred.
-	if android.IsModuleInVersionedSdk(ctx.Module()) {
-		return true
-	}
-
 	return false
 }
 
diff --git a/java/java.go b/java/java.go
index 0770f7f..e74185e 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1165,7 +1165,8 @@
 	properties ImportProperties
 
 	// output file containing classes.dex and resources
-	dexJarFile android.Path
+	dexJarFile        android.Path
+	dexJarInstallFile android.Path
 
 	combinedClasspathFile android.Path
 	classLoaderContexts   dexpreopt.ClassLoaderContextMap
@@ -1310,6 +1311,7 @@
 			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
 			if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil {
 				j.dexJarFile = dexOutputPath
+				j.dexJarInstallFile = android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName()))
 
 				// Initialize the hiddenapi structure.
 				j.initHiddenAPI(ctx, dexOutputPath, outputFile, nil)
@@ -1350,6 +1352,7 @@
 			dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile)
 
 			j.dexJarFile = dexOutputFile
+			j.dexJarInstallFile = android.PathForModuleInstall(ctx, "framework", jarName)
 		}
 	}
 
@@ -1391,7 +1394,7 @@
 }
 
 func (j *Import) DexJarInstallPath() android.Path {
-	return nil
+	return j.dexJarInstallFile
 }
 
 func (j *Import) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
diff --git a/java/lint.go b/java/lint.go
index dd5e4fb..fe3218e 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -391,8 +391,9 @@
 	rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
 
 	var apiVersionsName, apiVersionsPrebuilt string
-	if l.compileSdkKind == android.SdkModule {
-		// When compiling an SDK module we use the filtered database because otherwise lint's
+	if l.compileSdkKind == android.SdkModule || l.compileSdkKind == android.SdkSystemServer {
+		// When compiling an SDK module (or system server) we use the filtered
+		// database because otherwise lint's
 		// NewApi check produces too many false positives; This database excludes information
 		// about classes created in mainline modules hence removing those false positives.
 		apiVersionsName = "api_versions_public_filtered.xml"
diff --git a/java/lint_test.go b/java/lint_test.go
index 9cf1c33..456e6ba 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -261,6 +261,9 @@
 }
 
 func TestJavaLintDatabaseSelectionPublicFiltered(t *testing.T) {
+	testCases := []string{
+		"module_current", "system_server_current",
+	}
 	bp := `
 		java_library {
 			name: "foo",
@@ -274,17 +277,20 @@
 			},
 		}
 `
-	result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules).
-		RunTestWithBp(t, bp)
+	for _, testCase := range testCases {
+		thisBp := strings.Replace(bp, "XXX", testCase, 1)
+		result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules).
+			RunTestWithBp(t, thisBp)
 
-	foo := result.ModuleForTests("foo", "android_common")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command,
-		"/api_versions_public_filtered.xml") {
-		t.Error("did not use public-filtered lint api database", *sboxProto.Commands[0].Command)
-	}
-	if strings.Contains(*sboxProto.Commands[0].Command,
-		"/api_versions.xml") {
-		t.Error("used full api database")
+		foo := result.ModuleForTests("foo", "android_common")
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+		if !strings.Contains(*sboxProto.Commands[0].Command,
+			"/api_versions_public_filtered.xml") {
+			t.Error("did not use public-filtered lint api database for case", testCase)
+		}
+		if strings.Contains(*sboxProto.Commands[0].Command,
+			"/api_versions.xml") {
+			t.Error("used full api database for case", testCase)
+		}
 	}
 }
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index d72cee0..c1e14b2 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -437,7 +437,15 @@
 
 	// Build a profile for the image config and then use that to build the boot image.
 	profile := bootImageProfileRule(ctx, imageConfig)
-	buildBootImage(ctx, imageConfig, profile)
+
+	// Build boot image files for the android variants.
+	androidBootImageFilesByArch := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+
+	// Zip the android variant boot image files up.
+	buildBootImageZipInPredefinedLocation(ctx, imageConfig, androidBootImageFilesByArch)
+
+	// Build boot image files for the host variants. There are use directly by ART host side tests.
+	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
 
 	dumpOatRules(ctx, imageConfig)
 }
diff --git a/java/sdk_library.go b/java/sdk_library.go
index ed9aeff..2b71857 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1923,8 +1923,12 @@
 	// Is nil if the source module does not exist.
 	xmlPermissionsFileModule *sdkLibraryXml
 
-	// Path to the dex implementation jar obtained from the prebuilt_apex, if any.
+	// Build path to the dex implementation jar obtained from the prebuilt_apex, if any.
 	dexJarFile android.Path
+
+	// Expected install file path of the source module(sdk_library)
+	// or dex implementation jar obtained from the prebuilt_apex, if any.
+	installFile android.Path
 }
 
 var _ SdkLibraryDependency = (*SdkLibraryImport)(nil)
@@ -2136,6 +2140,9 @@
 
 	var deapexerModule android.Module
 
+	// Assume that source module(sdk_library) is installed in /<sdk_library partition>/framework
+	module.installFile = android.PathForModuleInstall(ctx, "framework", module.Stem()+".jar")
+
 	// Record the paths to the prebuilt stubs library and stubs source.
 	ctx.VisitDirectDeps(func(to android.Module) {
 		tag := ctx.OtherModuleDependencyTag(to)
@@ -2195,6 +2202,7 @@
 			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
 			if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil {
 				module.dexJarFile = dexOutputPath
+				module.installFile = android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName()))
 				module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
 			} else {
 				// This should never happen as a variant for a prebuilt_apex is only created if the
@@ -2249,11 +2257,7 @@
 
 // to satisfy UsesLibraryDependency interface
 func (module *SdkLibraryImport) DexJarInstallPath() android.Path {
-	if module.implLibraryModule == nil {
-		return nil
-	} else {
-		return module.implLibraryModule.DexJarInstallPath()
-	}
+	return module.installFile
 }
 
 // to satisfy UsesLibraryDependency interface
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 252c615..28a5a2c 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -106,21 +106,12 @@
 func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	// Convert content names to their appropriate stems, in case a test library is overriding an actual boot jar
-	var stems []string
-	for _, name := range s.properties.Contents {
-		dep := ctx.GetDirectDepWithTag(name, systemServerClasspathFragmentContentDepTag)
-		if m, ok := dep.(ModuleWithStem); ok {
-			stems = append(stems, m.Stem())
-		} else {
-			ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name)
-		}
-	}
+	possibleUpdatableModules := gatherPossibleUpdatableModuleNamesAndStems(ctx, s.properties.Contents, systemServerClasspathFragmentContentDepTag)
 
 	// Only create configs for updatable boot jars. Non-updatable system server jars must be part of the
 	// platform_systemserverclasspath's classpath proto config to guarantee that they come before any
 	// updatable jars at runtime.
-	return global.UpdatableSystemServerJars.Filter(stems)
+	return global.UpdatableSystemServerJars.Filter(possibleUpdatableModules)
 }
 
 type systemServerClasspathFragmentContentDependencyTag struct {
diff --git a/mk2rbc/android_products.go b/mk2rbc/android_products.go
new file mode 100644
index 0000000..de38391
--- /dev/null
+++ b/mk2rbc/android_products.go
@@ -0,0 +1,110 @@
+// Copyright 2021 Google LLC
+//
+// 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 mk2rbc
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	mkparser "android/soong/androidmk/parser"
+)
+
+// Implements mkparser.Scope, to be used by mkparser.Value.Value()
+type localDirEval struct {
+	localDir  string
+	hasErrors bool
+}
+
+func (l *localDirEval) Get(name string) string {
+	if name == "LOCAL_DIR" {
+		return l.localDir
+	}
+	l.hasErrors = true
+	return fmt.Sprintf("$(%s)", name)
+}
+
+func (l *localDirEval) Set(_, _ string) {
+}
+
+func (l *localDirEval) Call(_ string, _ []string) []string {
+	l.hasErrors = true
+	return []string{"$(call ...)"}
+}
+
+func (l *localDirEval) SetFunc(_ string, _ func([]string) []string) {
+}
+
+// UpdateProductConfigMap builds product configuration map.
+// The product configuration map maps a product name (i.e., the value of the
+// TARGET_PRODUCT variable) to the top-level configuration file.
+// In the Android's Make-based build machinery, the equivalent of the
+// product configuration map is $(PRODUCT_MAKEFILES), which is the list
+// of <product>:<configuration makefile> pairs (if <product>: is missing,
+// <product> is the basename of the configuration makefile).
+// UpdateProductConfigMap emulates this build logic by processing the
+// assignments to PRODUCT_MAKEFILES in the file passed to it.
+func UpdateProductConfigMap(configMap map[string]string, configMakefile string) error {
+	contents, err := ioutil.ReadFile(configMakefile)
+	if err != nil {
+		return err
+	}
+	parser := mkparser.NewParser(configMakefile, bytes.NewBuffer(contents))
+	nodes, errs := parser.Parse()
+	if len(errs) > 0 {
+		for _, e := range errs {
+			fmt.Fprintln(os.Stderr, "ERROR:", e)
+		}
+		return fmt.Errorf("cannot parse %s", configMakefile)
+	}
+
+	ldEval := &localDirEval{localDir: filepath.Dir(configMakefile)}
+
+	for _, node := range nodes {
+		// We are interested in assignments to 'PRODUCT_MAKEFILES'
+		asgn, ok := node.(*mkparser.Assignment)
+		if !ok {
+			continue
+		}
+		if !(asgn.Name.Const() && asgn.Name.Strings[0] == "PRODUCT_MAKEFILES") {
+			continue
+		}
+
+		// Resolve the references to $(LOCAL_DIR) in $(PRODUCT_MAKEFILES).
+		ldEval.hasErrors = false
+		value := asgn.Value.Value(ldEval)
+		if ldEval.hasErrors {
+			return fmt.Errorf("cannot evaluate %s", asgn.Value.Dump())
+		}
+		// Each item is either <product>:<configuration makefile>, or
+		// just <configuration makefile>
+		for _, token := range strings.Fields(value) {
+			var product, config_path string
+			if n := strings.Index(token, ":"); n >= 0 {
+				product = token[0:n]
+				config_path = token[n+1:]
+			} else {
+				config_path = token
+				product = filepath.Base(config_path)
+				product = strings.TrimSuffix(product, filepath.Ext(product))
+			}
+			configMap[product] = config_path
+		}
+	}
+	return nil
+}
diff --git a/mk2rbc/android_products_test.go b/mk2rbc/android_products_test.go
new file mode 100644
index 0000000..f8c930a
--- /dev/null
+++ b/mk2rbc/android_products_test.go
@@ -0,0 +1,38 @@
+// Copyright 2021 Google LLC
+//
+// 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 mk2rbc
+
+import (
+	"path/filepath"
+	"reflect"
+	"testing"
+)
+
+func TestProductsMakefile(t *testing.T) {
+	testDir := getTestDirectory()
+	abspath := func(relPath string) string { return filepath.Join(testDir, relPath) }
+	actualProducts := make(map[string]string)
+	if err := UpdateProductConfigMap(actualProducts, abspath("android_products.mk.test")); err != nil {
+		t.Fatal(err)
+	}
+	expectedProducts := map[string]string{
+		"aosp_cf_x86_tv": abspath("vsoc_x86/tv/device.mk"),
+		"aosp_tv_arm":    abspath("aosp_tv_arm.mk"),
+		"aosp_tv_arm64":  abspath("aosp_tv_arm64.mk"),
+	}
+	if !reflect.DeepEqual(actualProducts, expectedProducts) {
+		t.Errorf("\nExpected: %v\n  Actual: %v", expectedProducts, actualProducts)
+	}
+}
diff --git a/mk2rbc/config_variables.go b/mk2rbc/config_variables.go
new file mode 100644
index 0000000..dac509c
--- /dev/null
+++ b/mk2rbc/config_variables.go
@@ -0,0 +1,67 @@
+// Copyright 2021 Google LLC
+//
+// 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 mk2rbc
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	mkparser "android/soong/androidmk/parser"
+)
+
+// Extracts the list of product config variables from a file, calling
+// given registrar for each variable.
+func FindConfigVariables(mkFile string, vr variableRegistrar) error {
+	mkContents, err := ioutil.ReadFile(mkFile)
+	if err != nil {
+		return err
+	}
+	parser := mkparser.NewParser(mkFile, bytes.NewBuffer(mkContents))
+	nodes, errs := parser.Parse()
+	if len(errs) > 0 {
+		for _, e := range errs {
+			fmt.Fprintln(os.Stderr, "ERROR:", e)
+		}
+		return fmt.Errorf("cannot parse %s", mkFile)
+	}
+	for _, node := range nodes {
+		asgn, ok := node.(*mkparser.Assignment)
+		if !ok {
+			continue
+		}
+		// We are looking for a variable called '_product_list_vars'
+		// or '_product_single_value_vars'.
+		if !asgn.Name.Const() {
+			continue
+		}
+		varName := asgn.Name.Strings[0]
+		var starType starlarkType
+		if varName == "_product_list_vars" {
+			starType = starlarkTypeList
+		} else if varName == "_product_single_value_vars" {
+			starType = starlarkTypeUnknown
+		} else {
+			continue
+		}
+		for _, name := range strings.Fields(asgn.Value.Dump()) {
+			vr.NewVariable(name, VarClassConfig, starType)
+		}
+
+	}
+	return nil
+}
diff --git a/mk2rbc/config_variables_test.go b/mk2rbc/config_variables_test.go
new file mode 100644
index 0000000..f5a5180
--- /dev/null
+++ b/mk2rbc/config_variables_test.go
@@ -0,0 +1,60 @@
+// Copyright 2021 Google LLC
+//
+// 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 mk2rbc
+
+import (
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"testing"
+)
+
+type testVar struct {
+	name string
+	cl   varClass
+	ty   starlarkType
+}
+
+type testVariables struct {
+	v []testVar
+}
+
+func (v *testVariables) NewVariable(name string, varClass varClass, valueType starlarkType) {
+	v.v = append(v.v, testVar{name, varClass, valueType})
+}
+
+// getTestDirectory returns the test directory, which should be the test/ subdirectory
+func getTestDirectory() string {
+	_, myFile, _, _ := runtime.Caller(1)
+	return filepath.Join(filepath.Dir(myFile), "test")
+}
+
+func TestConfigVariables(t *testing.T) {
+	testFile := filepath.Join(getTestDirectory(), "config_variables.mk.test")
+	var actual testVariables
+	if err := FindConfigVariables(testFile, &actual); err != nil {
+		t.Fatal(err)
+	}
+	expected := testVariables{[]testVar{
+		{"PRODUCT_NAME", VarClassConfig, starlarkTypeUnknown},
+		{"PRODUCT_MODEL", VarClassConfig, starlarkTypeUnknown},
+		{"PRODUCT_LOCALES", VarClassConfig, starlarkTypeList},
+		{"PRODUCT_AAPT_CONFIG", VarClassConfig, starlarkTypeList},
+		{"PRODUCT_AAPT_PREF_CONFIG", VarClassConfig, starlarkTypeUnknown},
+	}}
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("\nExpected: %v\n  Actual: %v", expected, actual)
+	}
+}
diff --git a/mk2rbc/soong_variables.go b/mk2rbc/soong_variables.go
new file mode 100644
index 0000000..de46925
--- /dev/null
+++ b/mk2rbc/soong_variables.go
@@ -0,0 +1,151 @@
+// Copyright 2021 Google LLC
+//
+// 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 mk2rbc
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"regexp"
+	"strings"
+
+	mkparser "android/soong/androidmk/parser"
+)
+
+type context struct {
+	includeFileScope mkparser.Scope
+	registrar        variableRegistrar
+}
+
+// Scans the makefile Soong uses to generate soong.variables file,
+// collecting variable names and types from the lines that look like this:
+//    $(call add_json_XXX,  <...>,             $(VAR))
+//
+func FindSoongVariables(mkFile string, includeFileScope mkparser.Scope, registrar variableRegistrar) error {
+	ctx := context{includeFileScope, registrar}
+	return ctx.doFind(mkFile)
+}
+
+func (ctx *context) doFind(mkFile string) error {
+	mkContents, err := ioutil.ReadFile(mkFile)
+	if err != nil {
+		return err
+	}
+	parser := mkparser.NewParser(mkFile, bytes.NewBuffer(mkContents))
+	nodes, errs := parser.Parse()
+	if len(errs) > 0 {
+		for _, e := range errs {
+			fmt.Fprintln(os.Stderr, "ERROR:", e)
+		}
+		return fmt.Errorf("cannot parse %s", mkFile)
+	}
+	for _, node := range nodes {
+		switch t := node.(type) {
+		case *mkparser.Variable:
+			ctx.handleVariable(t)
+		case *mkparser.Directive:
+			ctx.handleInclude(t)
+		}
+	}
+	return nil
+}
+
+func (ctx context) NewSoongVariable(name, typeString string) {
+	var valueType starlarkType
+	switch typeString {
+	case "bool":
+		valueType = starlarkTypeBool
+	case "csv":
+		// Only PLATFORM_VERSION_ALL_CODENAMES, and it's a list
+		valueType = starlarkTypeList
+	case "list":
+		valueType = starlarkTypeList
+	case "str":
+		valueType = starlarkTypeString
+	case "val":
+		// Only PLATFORM_SDK_VERSION uses this, and it's integer
+		valueType = starlarkTypeInt
+	default:
+		panic(fmt.Errorf("unknown Soong variable type %s", typeString))
+	}
+
+	ctx.registrar.NewVariable(name, VarClassSoong, valueType)
+}
+
+func (ctx context) handleInclude(t *mkparser.Directive) {
+	if t.Name != "include" && t.Name != "-include" {
+		return
+	}
+	includedPath := t.Args.Value(ctx.includeFileScope)
+	err := ctx.doFind(includedPath)
+	if err != nil && t.Name == "include" {
+		fmt.Fprintf(os.Stderr, "cannot include %s: %s", includedPath, err)
+	}
+}
+
+var callFuncRex = regexp.MustCompile("^call +add_json_(str|val|bool|csv|list) *,")
+
+func (ctx context) handleVariable(t *mkparser.Variable) {
+	// From the variable reference looking as follows:
+	//  $(call json_add_TYPE,arg1,$(VAR))
+	// we infer that the type of $(VAR) is TYPE
+	// VAR can be a simple variable name, or another call
+	// (e.g., $(call invert_bool, $(X)), from which we can infer
+	// that the type of X is bool
+	if prefix, v, ok := prefixedVariable(t.Name); ok && strings.HasPrefix(prefix, "call add_json") {
+		if match := callFuncRex.FindStringSubmatch(prefix); match != nil {
+			ctx.inferSoongVariableType(match[1], v)
+			// NOTE(asmundak): sometimes arg1 (the name of the Soong variable defined
+			// in this statement) may indicate that there is a Make counterpart. E.g, from
+			//     $(call add_json_bool, DisablePreopt, $(call invert_bool,$(ENABLE_PREOPT)))
+			// it may be inferred that there is a Make boolean variable DISABLE_PREOPT.
+			// Unfortunately, Soong variable names have no 1:1 correspondence to Make variables,
+			// for instance,
+			//       $(call add_json_list, PatternsOnSystemOther, $(SYSTEM_OTHER_ODEX_FILTER))
+			// does not mean that there is PATTERNS_ON_SYSTEM_OTHER
+			// Our main interest lies in finding the variables whose values are lists, and
+			// so far there are none that can be found this way, so it is not important.
+		} else {
+			panic(fmt.Errorf("cannot match the call: %s", prefix))
+		}
+	}
+}
+
+var (
+	callInvertBoolRex = regexp.MustCompile("^call +invert_bool *, *$")
+	callFilterBoolRex = regexp.MustCompile("^(filter|filter-out) +(true|false), *$")
+)
+
+func (ctx context) inferSoongVariableType(vType string, n *mkparser.MakeString) {
+	if n.Const() {
+		ctx.NewSoongVariable(n.Strings[0], vType)
+		return
+	}
+	if prefix, v, ok := prefixedVariable(n); ok {
+		if callInvertBoolRex.MatchString(prefix) || callFilterBoolRex.MatchString(prefix) {
+			// It is $(call invert_bool, $(VAR)) or $(filter[-out] [false|true],$(VAR))
+			ctx.inferSoongVariableType("bool", v)
+		}
+	}
+}
+
+// If MakeString is foo$(BAR), returns 'foo', BAR(as *MakeString) and true
+func prefixedVariable(s *mkparser.MakeString) (string, *mkparser.MakeString, bool) {
+	if len(s.Strings) != 2 || s.Strings[1] != "" {
+		return "", nil, false
+	}
+	return s.Strings[0], s.Variables[0].Name, true
+}
diff --git a/mk2rbc/soong_variables_test.go b/mk2rbc/soong_variables_test.go
new file mode 100644
index 0000000..c883882
--- /dev/null
+++ b/mk2rbc/soong_variables_test.go
@@ -0,0 +1,51 @@
+// Copyright 2021 Google LLC
+//
+// 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 mk2rbc
+
+import (
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"testing"
+)
+
+type dirResolverForTest struct {
+	ScopeBase
+}
+
+func (t dirResolverForTest) Get(name string) string {
+	if name != "BUILD_SYSTEM" {
+		return fmt.Sprintf("$(%s)", name)
+	}
+	return getTestDirectory()
+}
+
+func TestSoongVariables(t *testing.T) {
+	testFile := filepath.Join(getTestDirectory(), "soong_variables.mk.test")
+	var actual testVariables
+	if err := FindSoongVariables(testFile, dirResolverForTest{}, &actual); err != nil {
+		t.Fatal(err)
+	}
+	expected := testVariables{[]testVar{
+		{"BUILD_ID", VarClassSoong, starlarkTypeString},
+		{"PLATFORM_SDK_VERSION", VarClassSoong, starlarkTypeInt},
+		{"DEVICE_PACKAGE_OVERLAYS", VarClassSoong, starlarkTypeList},
+		{"ENABLE_CFI", VarClassSoong, starlarkTypeBool},
+		{"ENABLE_PREOPT", VarClassSoong, starlarkTypeBool},
+	}}
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("\nExpected: %v\n  Actual: %v", expected, actual)
+	}
+}
diff --git a/mk2rbc/test/android_products.mk.test b/mk2rbc/test/android_products.mk.test
new file mode 100644
index 0000000..a2220ed
--- /dev/null
+++ b/mk2rbc/test/android_products.mk.test
@@ -0,0 +1,4 @@
+PRODUCT_MAKEFILES := \
+    $(LOCAL_DIR)/aosp_tv_arm.mk \
+    $(LOCAL_DIR)/aosp_tv_arm64.mk \
+    aosp_cf_x86_tv:$(LOCAL_DIR)/vsoc_x86/tv/device.mk
\ No newline at end of file
diff --git a/mk2rbc/test/config_variables.mk.test b/mk2rbc/test/config_variables.mk.test
new file mode 100644
index 0000000..e5cd0e9
--- /dev/null
+++ b/mk2rbc/test/config_variables.mk.test
@@ -0,0 +1,12 @@
+_product_single_value_vars :=
+
+# Variables that are lists of values.
+_product_list_vars :=
+
+_product_single_value_vars += PRODUCT_NAME
+_product_single_value_vars += PRODUCT_MODEL
+
+# The resoure configuration options to use for this product.
+_product_list_vars += PRODUCT_LOCALES
+_product_list_vars += PRODUCT_AAPT_CONFIG
+_product_single_value_vars += PRODUCT_AAPT_PREF_CONFIG
diff --git a/mk2rbc/test/soong_included.mk.test b/mk2rbc/test/soong_included.mk.test
new file mode 100644
index 0000000..255ecc5
--- /dev/null
+++ b/mk2rbc/test/soong_included.mk.test
@@ -0,0 +1 @@
+  $(call add_json_bool, DisablePreopt,                           $(call invert_bool,$(ENABLE_PREOPT)))
diff --git a/mk2rbc/test/soong_variables.mk.test b/mk2rbc/test/soong_variables.mk.test
new file mode 100644
index 0000000..ca60c9c
--- /dev/null
+++ b/mk2rbc/test/soong_variables.mk.test
@@ -0,0 +1,5 @@
+$(call add_json_str,  BuildId,                           $(BUILD_ID))
+$(call add_json_val,  Platform_sdk_version,              $(PLATFORM_SDK_VERSION))
+$(call add_json_list, DeviceResourceOverlays,            $(DEVICE_PACKAGE_OVERLAYS))
+$(call add_json_bool, EnableCFI,                         $(call invert_bool,$(filter false,$(ENABLE_CFI))))
+include $(BUILD_SYSTEM)/soong_included.mk.test
diff --git a/mk2rbc/types.go b/mk2rbc/types.go
new file mode 100644
index 0000000..22c8b58
--- /dev/null
+++ b/mk2rbc/types.go
@@ -0,0 +1,55 @@
+// Copyright 2021 Google LLC
+//
+// 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 mk2rbc
+
+// Starlark expression types we use
+type starlarkType int
+
+const (
+	starlarkTypeUnknown starlarkType = iota
+	starlarkTypeList    starlarkType = iota
+	starlarkTypeString  starlarkType = iota
+	starlarkTypeInt     starlarkType = iota
+	starlarkTypeBool    starlarkType = iota
+	starlarkTypeVoid    starlarkType = iota
+)
+
+type varClass int
+
+const (
+	VarClassConfig varClass = iota
+	VarClassSoong  varClass = iota
+	VarClassLocal  varClass = iota
+)
+
+type variableRegistrar interface {
+	NewVariable(name string, varClass varClass, valueType starlarkType)
+}
+
+// ScopeBase is a dummy implementation of the mkparser.Scope.
+// All our scopes are read-only and resolve only simple variables.
+type ScopeBase struct{}
+
+func (s ScopeBase) Set(_, _ string) {
+	panic("implement me")
+}
+
+func (s ScopeBase) Call(_ string, _ []string) []string {
+	panic("implement me")
+}
+
+func (s ScopeBase) SetFunc(_ string, _ func([]string) []string) {
+	panic("implement me")
+}
diff --git a/rust/binary.go b/rust/binary.go
index 8d0a0a7..2c3f548 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -155,7 +155,3 @@
 	}
 	return binary.baseCompiler.stdLinkage(ctx)
 }
-
-func (binary *binaryDecorator) isDependencyRoot() bool {
-	return true
-}
diff --git a/rust/compiler.go b/rust/compiler.go
index 1598ebf..0b28135 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -289,10 +289,6 @@
 	return android.OptionalPathForPath(compiler.cargoOutDir)
 }
 
-func (compiler *baseCompiler) isDependencyRoot() bool {
-	return false
-}
-
 func (compiler *baseCompiler) strippedOutputFilePath() android.OptionalPath {
 	return compiler.strippedOutputFile
 }
diff --git a/rust/rust.go b/rust/rust.go
index 05f6399..38f1742 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -87,6 +87,7 @@
 
 	// Used by vendor snapshot to record dependencies from snapshot modules.
 	SnapshotSharedLibs []string `blueprint:"mutated"`
+	SnapshotStaticLibs []string `blueprint:"mutated"`
 
 	// Make this module available when building for vendor ramdisk.
 	// On device without a dedicated recovery partition, the module is only
@@ -171,13 +172,6 @@
 	return mod.sanitize != nil && mod.compiler != nil
 }
 
-func (mod *Module) IsDependencyRoot() bool {
-	if mod.compiler != nil {
-		return mod.compiler.isDependencyRoot()
-	}
-	panic("IsDependencyRoot called on a non-compiler Rust module")
-}
-
 func (mod *Module) IsPrebuilt() bool {
 	if _, ok := mod.compiler.(*prebuiltLibraryDecorator); ok {
 		return true
@@ -259,6 +253,13 @@
 	return false
 }
 
+func (mod *Module) StaticExecutable() bool {
+	if !mod.Binary() {
+		return false
+	}
+	return Bool(mod.compiler.(*binaryDecorator).Properties.Static_executable)
+}
+
 func (mod *Module) Object() bool {
 	// Rust has no modules which produce only object files.
 	return false
@@ -441,7 +442,6 @@
 	SetDisabled()
 
 	stdLinkage(ctx *depsContext) RustLinkage
-	isDependencyRoot() bool
 
 	strippedOutputFilePath() android.OptionalPath
 }
@@ -990,7 +990,9 @@
 			case procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
-			case android.SourceDepTag:
+			}
+
+			if android.IsSourceDepTagWithOutputTag(depTag, "") {
 				// Since these deps are added in path_properties.go via AddDependencies, we need to ensure the correct
 				// OS/Arch variant is used.
 				var helper string
@@ -1097,6 +1099,7 @@
 
 				// Record baseLibName for snapshots.
 				mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName))
+				mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
 
 				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName)
 				exportDep = true
@@ -1119,8 +1122,7 @@
 		}
 
 		if srcDep, ok := dep.(android.SourceFileProducer); ok {
-			switch depTag {
-			case android.SourceDepTag:
+			if android.IsSourceDepTagWithOutputTag(depTag, "") {
 				// These are usually genrules which don't have per-target variants.
 				directSrcDeps = append(directSrcDeps, srcDep)
 			}
diff --git a/rust/snapshot_utils.go b/rust/snapshot_utils.go
index bd7ca7f..8dabd9b 100644
--- a/rust/snapshot_utils.go
+++ b/rust/snapshot_utils.go
@@ -57,6 +57,10 @@
 	return mod.Properties.SnapshotSharedLibs
 }
 
+func (mod *Module) SnapshotStaticLibs() []string {
+	return mod.Properties.SnapshotStaticLibs
+}
+
 func (mod *Module) Symlinks() []string {
 	// TODO update this to return the list of symlinks when Rust supports defining symlinks
 	return nil
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 98697dc..60fbccf 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -347,6 +347,7 @@
 		cc_object {
 			name: "crtobj",
 			stl: "none",
+			default_shared_libs: [],
 			sanitize: {
 				never: true,
 			},
@@ -364,6 +365,7 @@
     apex_available: ["//apex_available:platform"],
     stl: "none",
     compile_multilib: "both",
+    default_shared_libs: [],
     sanitize: {
         never: true,
     },
@@ -388,6 +390,7 @@
     apex_available: ["//apex_available:platform"],
     stl: "none",
     compile_multilib: "both",
+    default_shared_libs: [],
     sanitize: {
         never: true,
     },
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index a13b0d7..97fb248 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -565,6 +565,49 @@
 		)
 	})
 
+	t.Run("SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR=module:build_from_source", func(t *testing.T) {
+		result := android.GroupFixturePreparers(
+			preparer,
+			android.FixtureMergeEnv(map[string]string{
+				"SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR": "module:build_from_source",
+			}),
+		).RunTest(t)
+
+		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
+
+		CheckSnapshot(t, result, "mysdk", "",
+			checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    use_source_config_var: {
+        config_namespace: "module",
+        var_name: "build_from_source",
+    },
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/myjavalib.jar"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    visibility: ["//visibility:public"],
+    java_header_libs: ["mysdk_myjavalib@current"],
+}
+			`),
+		)
+	})
+
 	t.Run("SOONG_SDK_SNAPSHOT_VERSION=unversioned", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
 			preparer,
diff --git a/sdk/update.go b/sdk/update.go
index b146b62..6da3756 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -35,6 +35,37 @@
 //     By default every unversioned module in the generated snapshot has prefer: false. Building it
 //     with SOONG_SDK_SNAPSHOT_PREFER=true will force them to use prefer: true.
 //
+// SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR
+//     If set this specifies the Soong config var that can be used to control whether the prebuilt
+//     modules from the generated snapshot or the original source modules. Values must be a colon
+//     separated pair of strings, the first of which is the Soong config namespace, and the second
+//     is the name of the variable within that namespace.
+//
+//     The config namespace and var name are used to set the `use_source_config_var` property. That
+//     in turn will cause the generated prebuilts to use the soong config variable to select whether
+//     source or the prebuilt is used.
+//     e.g. If an sdk snapshot is built using:
+//       m SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR=acme:build_from_source sdkextensions-sdk
+//     Then the resulting snapshot will include:
+//       use_source_config_var: {
+//         config_namespace: "acme",
+//         var_name: "build_from_source",
+//       }
+//
+//     Assuming that the config variable is defined in .mk using something like:
+//       $(call add_soong_config_namespace,acme)
+//       $(call add_soong_config_var_value,acme,build_from_source,true)
+//
+//     Then when the snapshot is unpacked in the repository it will have the following behavior:
+//       m droid - will use the sdkextensions-sdk prebuilts if present. Otherwise, it will use the
+//           sources.
+//       m SOONG_CONFIG_acme_build_from_source=true droid - will use the sdkextensions-sdk
+//            sources, if present. Otherwise, it will use the prebuilts.
+//
+//     This is a temporary mechanism to control the prefer flags and will be removed once a more
+//     maintainable solution has been implemented.
+//     TODO(b/174997203): Remove when no longer necessary.
+//
 // SOONG_SDK_SNAPSHOT_VERSION
 //     This provides control over the version of the generated snapshot.
 //
@@ -760,6 +791,8 @@
 	module.insertAfter("name", "sdk_member_name", name)
 	// Remove the prefer property if present as versioned modules never need marking with prefer.
 	module.removeProperty("prefer")
+	// Ditto for use_source_config_var
+	module.removeProperty("use_source_config_var")
 	return module
 }
 
@@ -1627,13 +1660,24 @@
 		// snapshot to be created that sets prefer: true.
 		// TODO(b/174997203): Remove once the ability to select the modules to prefer can be done
 		//  dynamically at build time not at snapshot generation time.
-		prefer := ctx.sdkMemberContext.Config().IsEnvTrue("SOONG_SDK_SNAPSHOT_PREFER")
+		config := ctx.sdkMemberContext.Config()
+		prefer := config.IsEnvTrue("SOONG_SDK_SNAPSHOT_PREFER")
 
 		// Set prefer. Setting this to false is not strictly required as that is the default but it does
 		// provide a convenient hook to post-process the generated Android.bp file, e.g. in tests to
 		// check the behavior when a prebuilt is preferred. It also makes it explicit what the default
 		// behavior is for the module.
 		bpModule.insertAfter("name", "prefer", prefer)
+
+		configVar := config.Getenv("SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR")
+		if configVar != "" {
+			parts := strings.Split(configVar, ":")
+			cfp := android.ConfigVarProperties{
+				Config_namespace: proptools.StringPtr(parts[0]),
+				Var_name:         proptools.StringPtr(parts[1]),
+			}
+			bpModule.insertAfter("prefer", "use_source_config_var", cfp)
+		}
 	}
 
 	// Group the variants by os type.
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 83c8865..f3c442e 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -163,6 +163,7 @@
 	"AUX_OS_VARIANT_LIST",
 	"PRODUCT_SOONG_NAMESPACES",
 	"SOONG_SDK_SNAPSHOT_PREFER",
+	"SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR",
 	"SOONG_SDK_SNAPSHOT_VERSION",
 }