Merge "Share version settings with product config makefile."
diff --git a/android/androidmk.go b/android/androidmk.go
index 9853d2c..967c550 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -845,7 +845,7 @@
 		case "*selinux.selinuxContextsModule": // license properties written
 		case "*sysprop.syspropLibrary": // license properties written
 		default:
-			if ctx.Config().IsEnvTrue("ANDROID_REQUIRE_LICENSES") {
+			if !ctx.Config().IsEnvFalse("ANDROID_REQUIRE_LICENSES") {
 				return fmt.Errorf("custom make rules not allowed for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), ctx.ModuleName(mod))
 			}
 		}
diff --git a/android/arch.go b/android/arch.go
index f7eb963..ddc082b 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -631,8 +631,7 @@
 	image := base.commonProperties.ImageVariation
 	// Filter NativeBridge targets unless they are explicitly supported.
 	// Skip creating native bridge variants for non-core modules.
-	if os == Android &&
-		!(Bool(base.commonProperties.Native_bridge_supported) && image == CoreVariation) {
+	if os == Android && !(base.IsNativeBridgeSupported() && image == CoreVariation) {
 
 		var targets []Target
 		for _, t := range osTargets {
diff --git a/android/bazel.go b/android/bazel.go
index 373e292..8341e57 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -61,8 +61,8 @@
 	HandcraftedLabel() string
 	GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
 	ConvertWithBp2build(ctx BazelConversionPathContext) bool
+	convertWithBp2build(ctx BazelConversionPathContext, module blueprint.Module) bool
 	GetBazelBuildFileContents(c Config, path, name string) (string, error)
-	ConvertedToBazel(ctx BazelConversionPathContext) bool
 }
 
 // BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
@@ -175,6 +175,7 @@
 		"system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively,
 		"system/libbase":                  Bp2BuildDefaultTrueRecursively,
 		"system/logging/liblog":           Bp2BuildDefaultTrueRecursively,
+		"system/sepolicy/apex":            Bp2BuildDefaultTrueRecursively,
 		"system/timezone/apex":            Bp2BuildDefaultTrueRecursively,
 		"system/timezone/output_data":     Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
@@ -211,6 +212,13 @@
 		"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
 
+		// There are unexported symbols that don't surface on a shared library build,
+		// from the source static archive
+		// e.g. _Unwind_{GetRegionStart,GetLanguageSpecificData,GetIP,Set{IP,GR},Resume,{Raise,Delete}Exception}, pthread_atfork
+		// ... from: cxa_{personality,exception}.o, system_error.o, wrappers_c_bionic.o
+		// cf. http://b/198403271
+		"libc++",
+
 		// http://b/186823769: Needs C++ STL support, includes from unconverted standard libraries in //external/libcxx
 		// c++_static
 		"libbase_ndk", // http://b/186826477, cc_library, no such target '//build/bazel/platforms/os:darwin' when --platforms //build/bazel/platforms:android_x86 is added
@@ -222,6 +230,8 @@
 		"libbase",                  // Requires liblog. http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx.
 		// Also depends on fmtlib.
 
+		"libfdtrack", // depends on STL
+
 		"libseccomp_policy", // depends on libbase
 
 		"gwp_asan_crash_handler", // cc_library, ld.lld: error: undefined symbol: memset
@@ -311,9 +321,10 @@
 	if !ctx.Config().BazelContext.BazelEnabled() {
 		return false
 	}
-	if len(b.GetBazelLabel(ctx, ctx.Module())) == 0 {
+	if !convertedToBazel(ctx, ctx.Module()) {
 		return false
 	}
+
 	if GenerateCcLibraryStaticOnly(ctx) {
 		// Don't use partially-converted cc_library targets in mixed builds,
 		// since mixed builds would generally rely on both static and shared
@@ -323,20 +334,33 @@
 	return !mixedBuildsDisabled[ctx.Module().Name()]
 }
 
+// ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
+func convertedToBazel(ctx BazelConversionPathContext, module blueprint.Module) bool {
+	b, ok := module.(Bazelable)
+	if !ok {
+		return false
+	}
+	return b.convertWithBp2build(ctx, module) || b.HasHandcraftedLabel()
+}
+
 // ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
 func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool {
-	if bp2buildModuleDoNotConvert[ctx.Module().Name()] {
+	return b.convertWithBp2build(ctx, ctx.Module())
+}
+
+func (b *BazelModuleBase) convertWithBp2build(ctx BazelConversionPathContext, module blueprint.Module) bool {
+	if bp2buildModuleDoNotConvert[module.Name()] {
 		return false
 	}
 
 	// Ensure that the module type of this module has a bp2build converter. This
 	// prevents mixed builds from using auto-converted modules just by matching
 	// the package dir; it also has to have a bp2build mutator as well.
-	if ctx.Config().bp2buildModuleTypeConfig[ctx.ModuleType()] == false {
+	if ctx.Config().bp2buildModuleTypeConfig[ctx.OtherModuleType(module)] == false {
 		return false
 	}
 
-	packagePath := ctx.ModuleDir()
+	packagePath := ctx.OtherModuleDir(module)
 	config := ctx.Config().bp2buildPackageConfig
 
 	// This is a tristate value: true, false, or unset.
@@ -407,9 +431,3 @@
 	}
 	return string(data[:]), nil
 }
-
-// ConvertedToBazel returns whether this module has been converted to Bazel, whether automatically
-// or manually
-func (b *BazelModuleBase) ConvertedToBazel(ctx BazelConversionPathContext) bool {
-	return b.ConvertWithBp2build(ctx) || b.HasHandcraftedLabel()
-}
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index a4bd2ef..ccbc156 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -75,9 +75,10 @@
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 	ModuleFromName(name string) (blueprint.Module, bool)
 	Module() Module
-	ModuleType() string
+	OtherModuleType(m blueprint.Module) string
 	OtherModuleName(m blueprint.Module) string
 	OtherModuleDir(m blueprint.Module) string
+	AddUnconvertedBp2buildDep(string)
 }
 
 // BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
@@ -345,6 +346,9 @@
 	if m == nil {
 		panic(fmt.Errorf("No module named %q found, but was a direct dep of %q", dep, ctx.Module().Name()))
 	}
+	if !convertedToBazel(ctx, m) {
+		ctx.AddUnconvertedBp2buildDep(dep)
+	}
 	otherLabel := bazelModuleLabel(ctx, m, tag)
 	label := bazelModuleLabel(ctx, ctx.Module(), "")
 	if isWholeLibs {
@@ -363,11 +367,10 @@
 
 func bazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module, tag string) string {
 	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
-	b, ok := module.(Bazelable)
-	// TODO(b/181155349): perhaps return an error here if the module can't be/isn't being converted
-	if !ok || !b.ConvertedToBazel(ctx) {
+	if !convertedToBazel(ctx, module) {
 		return bp2buildModuleLabel(ctx, module)
 	}
+	b, _ := module.(Bazelable)
 	return b.GetBazelLabel(ctx, module)
 }
 
diff --git a/android/config.go b/android/config.go
index 0767e7b..993aaa7 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1531,6 +1531,10 @@
 		c.config.productVariables.RecoverySnapshotDirsIncluded)
 }
 
+func (c *deviceConfig) HostFakeSnapshotEnabled() bool {
+	return c.config.productVariables.HostFakeSnapshotEnabled
+}
+
 func (c *deviceConfig) ShippingApiLevel() ApiLevel {
 	if c.config.productVariables.ShippingApiLevel == nil {
 		return NoneApiLevel
@@ -1655,6 +1659,20 @@
 	return ConfiguredJarList{apexes, jars}
 }
 
+// Append a list of (apex, jar) pairs to the list.
+func (l *ConfiguredJarList) AppendList(other ConfiguredJarList) ConfiguredJarList {
+	apexes := make([]string, 0, l.Len()+other.Len())
+	jars := make([]string, 0, l.Len()+other.Len())
+
+	apexes = append(apexes, l.apexes...)
+	jars = append(jars, l.jars...)
+
+	apexes = append(apexes, other.apexes...)
+	jars = append(jars, other.jars...)
+
+	return ConfiguredJarList{apexes, jars}
+}
+
 // RemoveList filters out a list of (apex, jar) pairs from the receiving list of pairs.
 func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) ConfiguredJarList {
 	apexes := make([]string, 0, l.Len())
diff --git a/android/deapexer.go b/android/deapexer.go
index de3f635..9290481 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -73,7 +73,7 @@
 	// exported file name is the apex relative path, e.g. javalib/core-libart.jar.
 	//
 	// See Prebuilt.ApexInfoMutator for more information.
-	exports map[string]Path
+	exports map[string]WritablePath
 }
 
 // PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
@@ -82,7 +82,7 @@
 // The exported file is identified by the apex relative path, e.g. "javalib/core-libart.jar".
 //
 // See apex/deapexer.go for more information.
-func (i DeapexerInfo) PrebuiltExportPath(apexRelativePath string) Path {
+func (i DeapexerInfo) PrebuiltExportPath(apexRelativePath string) WritablePath {
 	path := i.exports[apexRelativePath]
 	return path
 }
@@ -95,7 +95,7 @@
 // for use with a prebuilt_apex module.
 //
 // See apex/deapexer.go for more information.
-func NewDeapexerInfo(exports map[string]Path) DeapexerInfo {
+func NewDeapexerInfo(exports map[string]WritablePath) DeapexerInfo {
 	return DeapexerInfo{
 		exports: exports,
 	}
diff --git a/android/defs.go b/android/defs.go
index b3ff376..c8e2e9b 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -188,6 +188,15 @@
 	buildWriteFileRule(ctx, outputFile, content)
 }
 
+func CatFileRule(ctx BuilderContext, paths Paths, outputFile WritablePath) {
+	ctx.Build(pctx, BuildParams{
+		Rule:        Cat,
+		Inputs:      paths,
+		Output:      outputFile,
+		Description: "combine files to " + outputFile.Base(),
+	})
+}
+
 // shellUnescape reverses proptools.ShellEscape
 func shellUnescape(s string) string {
 	// Remove leading and trailing quotes if present
diff --git a/android/licenses.go b/android/licenses.go
index d54f8f4..bcd85f9 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -48,7 +48,7 @@
 
 	// License modules, i.e. modules depended upon via a licensesTag, must be automatically added to
 	// any sdk/module_exports to which their referencing module is a member.
-	_ SdkMemberTypeDependencyTag = licensesTag
+	_ SdkMemberDependencyTag = licensesTag
 )
 
 // Describes the property provided by a module to reference applicable licenses.
@@ -253,7 +253,7 @@
 
 	primaryProperty := module.base().primaryLicensesProperty
 	if primaryProperty == nil {
-		if ctx.Config().IsEnvTrue("ANDROID_REQUIRE_LICENSES") {
+		if !ctx.Config().IsEnvFalse("ANDROID_REQUIRE_LICENSES") {
 			ctx.ModuleErrorf("module type %q must have an applicable licenses property", ctx.OtherModuleType(module))
 		}
 		return nil
diff --git a/android/module.go b/android/module.go
index dd6a25a..2803455 100644
--- a/android/module.go
+++ b/android/module.go
@@ -316,6 +316,9 @@
 
 	AddMissingDependencies(missingDeps []string)
 
+	// AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build
+	AddUnconvertedBp2buildDep(dep string)
+
 	Target() Target
 	TargetPrimary() bool
 
@@ -496,6 +499,7 @@
 	IsConvertedByBp2build() bool
 	// Bp2buildTargets returns the target(s) generated for Bazel via bp2build for this module
 	Bp2buildTargets() []bp2buildInfo
+	GetUnconvertedBp2buildDeps() []string
 
 	BuildParamsForTests() []BuildParams
 	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
@@ -833,6 +837,10 @@
 	// supported as Soong handles some things within a single target that we may choose to split into
 	// multiple targets, e.g. renderscript, protos, yacc within a cc module.
 	Bp2buildInfo []bp2buildInfo `blueprint:"mutated"`
+
+	// UnconvertedBp2buildDep stores the module names of direct dependency that were not converted to
+	// Bazel
+	UnconvertedBp2buildDeps []string `blueprint:"mutated"`
 }
 
 type distProperties struct {
@@ -1212,6 +1220,18 @@
 	return m.commonProperties.Bp2buildInfo
 }
 
+// AddUnconvertedBp2buildDep stores module name of a dependency that was not converted to Bazel.
+func (b *baseModuleContext) AddUnconvertedBp2buildDep(dep string) {
+	unconvertedDeps := &b.Module().base().commonProperties.UnconvertedBp2buildDeps
+	*unconvertedDeps = append(*unconvertedDeps, dep)
+}
+
+// GetUnconvertedBp2buildDeps returns the list of module names of this module's direct dependencies that
+// were not converted to Bazel.
+func (m *ModuleBase) GetUnconvertedBp2buildDeps() []string {
+	return m.commonProperties.UnconvertedBp2buildDeps
+}
+
 func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
 	(*d)["Android"] = map[string]interface{}{}
 }
@@ -1229,7 +1249,30 @@
 }
 
 func (m *ModuleBase) BuildParamsForTests() []BuildParams {
-	return m.buildParams
+	// Expand the references to module variables like $flags[0-9]*,
+	// so we do not need to change many existing unit tests.
+	// This looks like undoing the shareFlags optimization in cc's
+	// transformSourceToObj, and should only affects unit tests.
+	vars := m.VariablesForTests()
+	buildParams := append([]BuildParams(nil), m.buildParams...)
+	for i, _ := range buildParams {
+		newArgs := make(map[string]string)
+		for k, v := range buildParams[i].Args {
+			newArgs[k] = v
+			// Replaces both ${flags1} and $flags1 syntax.
+			if strings.HasPrefix(v, "${") && strings.HasSuffix(v, "}") {
+				if value, found := vars[v[2:len(v)-1]]; found {
+					newArgs[k] = value
+				}
+			} else if strings.HasPrefix(v, "$") {
+				if value, found := vars[v[1:]]; found {
+					newArgs[k] = value
+				}
+			}
+		}
+		buildParams[i].Args = newArgs
+	}
+	return buildParams
 }
 
 func (m *ModuleBase) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
diff --git a/android/mutator.go b/android/mutator.go
index 20ec621..b361c51 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -16,9 +16,7 @@
 
 import (
 	"android/soong/bazel"
-	"fmt"
 	"reflect"
-	"strings"
 	"sync"
 
 	"github.com/google/blueprint"
@@ -519,12 +517,6 @@
 	name string,
 	bazelProps bazel.BazelTargetModuleProperties,
 	attrs interface{}) {
-	if strings.HasPrefix(name, bazel.BazelTargetModuleNamePrefix) {
-		panic(fmt.Errorf(
-			"The %s name prefix is added automatically, do not set it manually: %s",
-			bazel.BazelTargetModuleNamePrefix,
-			name))
-	}
 
 	info := bp2buildInfo{
 		Name:       name,
@@ -532,7 +524,6 @@
 		BazelProps: bazelProps,
 		Attrs:      attrs,
 	}
-
 	t.Module().base().addBp2buildInfo(info)
 }
 
diff --git a/android/paths.go b/android/paths.go
index 763cd7c..2e378ba 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -263,38 +263,56 @@
 
 // OptionalPath is a container that may or may not contain a valid Path.
 type OptionalPath struct {
-	valid bool
-	path  Path
+	path          Path   // nil if invalid.
+	invalidReason string // Not applicable if path != nil. "" if the reason is unknown.
 }
 
 // OptionalPathForPath returns an OptionalPath containing the path.
 func OptionalPathForPath(path Path) OptionalPath {
-	if path == nil {
-		return OptionalPath{}
-	}
-	return OptionalPath{valid: true, path: path}
+	return OptionalPath{path: path}
+}
+
+// InvalidOptionalPath returns an OptionalPath that is invalid with the given reason.
+func InvalidOptionalPath(reason string) OptionalPath {
+
+	return OptionalPath{invalidReason: reason}
 }
 
 // Valid returns whether there is a valid path
 func (p OptionalPath) Valid() bool {
-	return p.valid
+	return p.path != nil
 }
 
 // Path returns the Path embedded in this OptionalPath. You must be sure that
 // there is a valid path, since this method will panic if there is not.
 func (p OptionalPath) Path() Path {
-	if !p.valid {
-		panic("Requesting an invalid path")
+	if p.path == nil {
+		msg := "Requesting an invalid path"
+		if p.invalidReason != "" {
+			msg += ": " + p.invalidReason
+		}
+		panic(msg)
 	}
 	return p.path
 }
 
+// InvalidReason returns the reason that the optional path is invalid, or "" if it is valid.
+func (p OptionalPath) InvalidReason() string {
+	if p.path != nil {
+		return ""
+	}
+	if p.invalidReason == "" {
+		return "unknown"
+	}
+	return p.invalidReason
+}
+
 // AsPaths converts the OptionalPath into Paths.
 //
 // It returns nil if this is not valid, or a single length slice containing the Path embedded in
 // this OptionalPath.
 func (p OptionalPath) AsPaths() Paths {
-	if !p.valid {
+	if p.path == nil {
 		return nil
 	}
 	return Paths{p.path}
@@ -303,7 +321,7 @@
 // RelativeToTop returns an OptionalPath with the path that was embedded having been replaced by the
 // result of calling Path.RelativeToTop on it.
 func (p OptionalPath) RelativeToTop() OptionalPath {
-	if !p.valid {
+	if p.path == nil {
 		return p
 	}
 	p.path = p.path.RelativeToTop()
@@ -312,7 +330,7 @@
 
 // String returns the string version of the Path, or "" if it isn't valid.
 func (p OptionalPath) String() string {
-	if p.valid {
+	if p.path != nil {
 		return p.path.String()
 	} else {
 		return ""
@@ -1077,6 +1095,7 @@
 	path, err := pathForSource(ctx, pathComponents...)
 	if err != nil {
 		reportPathError(ctx, err)
+		// No need to put the error message into the returned path since it has been reported already.
 		return OptionalPath{}
 	}
 
@@ -1091,7 +1110,7 @@
 		return OptionalPath{}
 	}
 	if !exists {
-		return OptionalPath{}
+		return InvalidOptionalPath(path.String() + " does not exist")
 	}
 	return OptionalPathForPath(path)
 }
@@ -1127,6 +1146,7 @@
 		relDir = srcPath.path
 	} else {
 		ReportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
+		// No need to put the error message into the returned path since it has been reported already.
 		return OptionalPath{}
 	}
 	dir := filepath.Join(p.srcDir, p.path, relDir)
@@ -1140,7 +1160,7 @@
 		return OptionalPath{}
 	}
 	if len(paths) == 0 {
-		return OptionalPath{}
+		return InvalidOptionalPath(dir + " does not exist")
 	}
 	relPath := Rel(ctx, p.srcDir, paths[0])
 	return OptionalPathForPath(PathForSource(ctx, relPath))
diff --git a/android/paths_test.go b/android/paths_test.go
index f4e4ce1..3f4625d 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -137,26 +137,35 @@
 
 func TestOptionalPath(t *testing.T) {
 	var path OptionalPath
-	checkInvalidOptionalPath(t, path)
+	checkInvalidOptionalPath(t, path, "unknown")
 
 	path = OptionalPathForPath(nil)
-	checkInvalidOptionalPath(t, path)
+	checkInvalidOptionalPath(t, path, "unknown")
+
+	path = InvalidOptionalPath("foo")
+	checkInvalidOptionalPath(t, path, "foo")
+
+	path = InvalidOptionalPath("")
+	checkInvalidOptionalPath(t, path, "unknown")
 
 	path = OptionalPathForPath(PathForTesting("path"))
 	checkValidOptionalPath(t, path, "path")
 }
 
-func checkInvalidOptionalPath(t *testing.T, path OptionalPath) {
+func checkInvalidOptionalPath(t *testing.T, path OptionalPath, expectedInvalidReason string) {
 	t.Helper()
 	if path.Valid() {
-		t.Errorf("Uninitialized OptionalPath should not be valid")
+		t.Errorf("Invalid OptionalPath should not be valid")
+	}
+	if path.InvalidReason() != expectedInvalidReason {
+		t.Errorf("Wrong invalid reason: expected %q, got %q", expectedInvalidReason, path.InvalidReason())
 	}
 	if path.String() != "" {
-		t.Errorf("Uninitialized OptionalPath String() should return \"\", not %q", path.String())
+		t.Errorf("Invalid OptionalPath String() should return \"\", not %q", path.String())
 	}
 	paths := path.AsPaths()
 	if len(paths) != 0 {
-		t.Errorf("Uninitialized OptionalPath AsPaths() should return empty Paths, not %q", paths)
+		t.Errorf("Invalid OptionalPath AsPaths() should return empty Paths, not %q", paths)
 	}
 	defer func() {
 		if r := recover(); r == nil {
@@ -171,6 +180,9 @@
 	if !path.Valid() {
 		t.Errorf("Initialized OptionalPath should not be invalid")
 	}
+	if path.InvalidReason() != "" {
+		t.Errorf("Initialized OptionalPath should not have an invalid reason, got: %q", path.InvalidReason())
+	}
 	if path.String() != expectedString {
 		t.Errorf("Initialized OptionalPath String() should return %q, not %q", expectedString, path.String())
 	}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index e611502..e189892 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -240,7 +240,7 @@
 			value = value.Elem()
 		}
 		if value.Kind() != reflect.String {
-			panic(fmt.Errorf("prebuilt src field %q should be a string or a pointer to one but was %d %q", srcPropertyName, value.Kind(), value))
+			panic(fmt.Errorf("prebuilt src field %q in %T in module %s should be a string or a pointer to one but was %v", srcField, srcProps, module, value))
 		}
 		src := value.String()
 		if src == "" {
diff --git a/android/sdk.go b/android/sdk.go
index b8f76c1..100f63b 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -22,6 +22,8 @@
 	"github.com/google/blueprint/proptools"
 )
 
+// RequiredSdks provides access to the set of SDKs required by an APEX and its contents.
+//
 // Extracted from SdkAware to make it easier to define custom subsets of the
 // SdkAware interface and improve code navigation within the IDE.
 //
@@ -30,11 +32,11 @@
 // is expected to implement RequiredSdks() by reading its own properties like
 // `uses_sdks`.
 type RequiredSdks interface {
-	// The set of SDKs required by an APEX and its contents.
+	// RequiredSdks returns the set of SDKs required by an APEX and its contents.
 	RequiredSdks() SdkRefs
 }
 
-// Provided to improve code navigation with the IDE.
+// sdkAwareWithoutModule is provided simply to improve code navigation with the IDE.
 type sdkAwareWithoutModule interface {
 	RequiredSdks
 
@@ -233,75 +235,85 @@
 	return false
 }
 
-// Provide support for generating the build rules which will build the snapshot.
+// SnapshotBuilder provides support for generating the build rules which will build the snapshot.
 type SnapshotBuilder interface {
-	// Copy src to the dest (which is a snapshot relative path) and add the dest
-	// to the zip
+	// CopyToSnapshot generates a rule that will copy the src to the dest (which is a snapshot
+	// relative path) and add the dest to the zip.
 	CopyToSnapshot(src Path, dest string)
 
-	// Return the path to an empty file.
+	// EmptyFile returns the path to an empty file.
 	//
 	// This can be used by sdk member types that need to create an empty file in the snapshot, simply
 	// pass the value returned from this to the CopyToSnapshot() method.
 	EmptyFile() Path
 
-	// Unzip the supplied zip into the snapshot relative directory destDir.
+	// UnzipToSnapshot generates a rule that will unzip the supplied zip into the snapshot relative
+	// directory destDir.
 	UnzipToSnapshot(zipPath Path, destDir string)
 
-	// Add a new prebuilt module to the snapshot. The returned module
-	// must be populated with the module type specific properties. The following
-	// properties will be automatically populated.
+	// AddPrebuiltModule adds a new prebuilt module to the snapshot.
+	//
+	// It is intended to be called from SdkMemberType.AddPrebuiltModule which can add module type
+	// specific properties that are not variant specific. The following properties will be
+	// automatically populated before returning.
 	//
 	// * name
 	// * sdk_member_name
 	// * prefer
 	//
-	// This will result in two Soong modules being generated in the Android. One
-	// that is versioned, coupled to the snapshot version and marked as
-	// prefer=true. And one that is not versioned, not marked as prefer=true and
-	// will only be used if the equivalently named non-prebuilt module is not
-	// present.
+	// Properties that are variant specific will be handled by SdkMemberProperties structure.
+	//
+	// Each module created by this method can be output to the generated Android.bp file in two
+	// different forms, depending on the setting of the SOONG_SDK_SNAPSHOT_VERSION build property.
+	// The two forms are:
+	// 1. A versioned Soong module that is referenced from a corresponding similarly versioned
+	//    snapshot module.
+	// 2. An unversioned Soong module that.
+	//
+	// See sdk/update.go for more information.
 	AddPrebuiltModule(member SdkMember, moduleType string) BpModule
 
-	// The property tag to use when adding a property to a BpModule that contains
-	// references to other sdk members. Using this will ensure that the reference
-	// is correctly output for both versioned and unversioned prebuilts in the
-	// snapshot.
+	// SdkMemberReferencePropertyTag returns a property tag to use when adding a property to a
+	// BpModule that contains references to other sdk members.
 	//
-	// "required: true" means that the property must only contain references
-	// to other members of the sdk. Passing a reference to a module that is not a
-	// member of the sdk will result in a build error.
+	// Using this will ensure that the reference is correctly output for both versioned and
+	// unversioned prebuilts in the snapshot.
 	//
-	// "required: false" means that the property can contain references to modules
-	// that are either members or not members of the sdk. If a reference is to a
-	// module that is a non member then the reference is left unchanged, i.e. it
-	// is not transformed as references to members are.
+	// "required: true" means that the property must only contain references to other members of the
+	// sdk. Passing a reference to a module that is not a member of the sdk will result in a build
+	// error.
 	//
-	// The handling of the member names is dependent on whether it is an internal or
-	// exported member. An exported member is one whose name is specified in one of
-	// the member type specific properties. An internal member is one that is added
-	// due to being a part of an exported (or other internal) member and is not itself
-	// an exported member.
+	// "required: false" means that the property can contain references to modules that are either
+	// members or not members of the sdk. If a reference is to a module that is a non member then the
+	// reference is left unchanged, i.e. it is not transformed as references to members are.
+	//
+	// The handling of the member names is dependent on whether it is an internal or exported member.
+	// An exported member is one whose name is specified in one of the member type specific
+	// properties. An internal member is one that is added due to being a part of an exported (or
+	// other internal) member and is not itself an exported member.
 	//
 	// Member names are handled as follows:
-	// * When creating the unversioned form of the module the name is left unchecked
-	//   unless the member is internal in which case it is transformed into an sdk
-	//   specific name, i.e. by prefixing with the sdk name.
+	// * When creating the unversioned form of the module the name is left unchecked unless the member
+	//   is internal in which case it is transformed into an sdk specific name, i.e. by prefixing with
+	//   the sdk name.
 	//
-	// * When creating the versioned form of the module the name is transformed into
-	//   a versioned sdk specific name, i.e. by prefixing with the sdk name and
-	//   suffixing with the version.
+	// * When creating the versioned form of the module the name is transformed into a versioned sdk
+	//   specific name, i.e. by prefixing with the sdk name and suffixing with the version.
 	//
 	// e.g.
 	// bpPropertySet.AddPropertyWithTag("libs", []string{"member1", "member2"}, builder.SdkMemberReferencePropertyTag(true))
 	SdkMemberReferencePropertyTag(required bool) BpPropertyTag
 }
 
+// BpPropertyTag is a marker interface that can be associated with properties in a BpPropertySet to
+// provide additional information which can be used to customize their behavior.
 type BpPropertyTag interface{}
 
-// A set of properties for use in a .bp file.
+// BpPropertySet is a set of properties for use in a .bp file.
 type BpPropertySet interface {
-	// Add a property, the value can be one of the following types:
+	// AddProperty adds a property.
+	//
+	// The value can be one of the following types:
 	// * string
 	// * array of the above
 	// * bool
@@ -326,18 +338,18 @@
 	// * Otherwise, if a property exists with the name then it is an error.
 	AddProperty(name string, value interface{})
 
-	// Add a property with an associated tag
+	// AddPropertyWithTag adds a property with an associated property tag.
 	AddPropertyWithTag(name string, value interface{}, tag BpPropertyTag)
 
-	// Add a property set with the specified name and return so that additional
-	// properties can be added.
+	// AddPropertySet adds a property set with the specified name and returns it so that additional
+	// properties can be added to it.
 	AddPropertySet(name string) BpPropertySet
 
-	// Add comment for property (or property set).
+	// AddCommentForProperty adds a comment for the named property (or property set).
 	AddCommentForProperty(name, text string)
 }
 
-// A .bp module definition.
+// BpModule represents a module definition in a .bp file.
 type BpModule interface {
 	BpPropertySet
 
@@ -364,19 +376,20 @@
 
 var _ BpPrintable = BpPrintableBase{}
 
-// An individual member of the SDK, includes all of the variants that the SDK
-// requires.
+// SdkMember is an individual member of the SDK.
+//
+// It includes all of the variants that the SDK depends upon.
 type SdkMember interface {
-	// The name of the member.
+	// Name returns the name of the member.
 	Name() string
 
-	// All the variants required by the SDK.
+	// Variants returns all the variants of this module depended upon by the SDK.
 	Variants() []SdkAware
 }
 
-// SdkMemberTypeDependencyTag is the interface that a tag must implement in order to allow the
+// SdkMemberDependencyTag is the interface that a tag must implement in order to allow the
 // dependent module to be automatically added to the sdk.
-type SdkMemberTypeDependencyTag interface {
+type SdkMemberDependencyTag interface {
 	blueprint.DependencyTag
 
 	// SdkMemberType returns the SdkMemberType that will be used to automatically add the child module
@@ -401,37 +414,37 @@
 	ExportMember() bool
 }
 
-var _ SdkMemberTypeDependencyTag = (*sdkMemberTypeDependencyTag)(nil)
-var _ ReplaceSourceWithPrebuilt = (*sdkMemberTypeDependencyTag)(nil)
+var _ SdkMemberDependencyTag = (*sdkMemberDependencyTag)(nil)
+var _ ReplaceSourceWithPrebuilt = (*sdkMemberDependencyTag)(nil)
 
-type sdkMemberTypeDependencyTag struct {
+type sdkMemberDependencyTag struct {
 	blueprint.BaseDependencyTag
 	memberType SdkMemberType
 	export     bool
 }
 
-func (t *sdkMemberTypeDependencyTag) SdkMemberType(_ Module) SdkMemberType {
+func (t *sdkMemberDependencyTag) SdkMemberType(_ Module) SdkMemberType {
 	return t.memberType
 }
 
-func (t *sdkMemberTypeDependencyTag) ExportMember() bool {
+func (t *sdkMemberDependencyTag) ExportMember() bool {
 	return t.export
 }
 
-// Prevent dependencies from the sdk/module_exports onto their members from being
-// replaced with a preferred prebuilt.
-func (t *sdkMemberTypeDependencyTag) ReplaceSourceWithPrebuilt() bool {
+// ReplaceSourceWithPrebuilt prevents dependencies from the sdk/module_exports onto their members
+// from being replaced with a preferred prebuilt.
+func (t *sdkMemberDependencyTag) ReplaceSourceWithPrebuilt() bool {
 	return false
 }
 
-// DependencyTagForSdkMemberType creates an SdkMemberTypeDependencyTag that will cause any
+// DependencyTagForSdkMemberType creates an SdkMemberDependencyTag that will cause any
 // dependencies added by the tag to be added to the sdk as the specified SdkMemberType and exported
 // (or not) as specified by the export parameter.
-func DependencyTagForSdkMemberType(memberType SdkMemberType, export bool) SdkMemberTypeDependencyTag {
-	return &sdkMemberTypeDependencyTag{memberType: memberType, export: export}
+func DependencyTagForSdkMemberType(memberType SdkMemberType, export bool) SdkMemberDependencyTag {
+	return &sdkMemberDependencyTag{memberType: memberType, export: export}
 }
 
-// Interface that must be implemented for every type that can be a member of an
+// SdkMemberType is the interface that must be implemented for every type that can be a member of an
 // sdk.
 //
 // The basic implementation should look something like this, where ModuleType is
@@ -452,43 +465,43 @@
 //    ...methods...
 //
 type SdkMemberType interface {
-	// The name of the member type property on an sdk module.
+	// SdkPropertyName returns the name of the member type property on an sdk module.
 	SdkPropertyName() string
 
 	// RequiresBpProperty returns true if this member type requires its property to be usable within
 	// an Android.bp file.
 	RequiresBpProperty() bool
 
-	// True if the member type supports the sdk/sdk_snapshot, false otherwise.
+	// UsableWithSdkAndSdkSnapshot returns true if the member type supports the sdk/sdk_snapshot,
+	// false otherwise.
 	UsableWithSdkAndSdkSnapshot() bool
 
-	// Return true if prebuilt host artifacts may be specific to the host OS. Only
-	// applicable to modules where HostSupported() is true. If this is true,
-	// snapshots will list each host OS variant explicitly and disable all other
-	// host OS'es.
+	// IsHostOsDependent returns true if prebuilt host artifacts may be specific to the host OS. Only
+	// applicable to modules where HostSupported() is true. If this is true, snapshots will list each
+	// host OS variant explicitly and disable all other host OS'es.
 	IsHostOsDependent() bool
 
-	// Add dependencies from the SDK module to all the module variants the member
-	// type contributes to the SDK. `names` is the list of module names given in
-	// the member type property (as returned by SdkPropertyName()) in the SDK
-	// module. The exact set of variants required is determined by the SDK and its
-	// properties. The dependencies must be added with the supplied tag.
+	// AddDependencies adds dependencies from the SDK module to all the module variants the member
+	// type contributes to the SDK. `names` is the list of module names given in the member type
+	// property (as returned by SdkPropertyName()) in the SDK module. The exact set of variants
+	// required is determined by the SDK and its properties. The dependencies must be added with the
+	// supplied tag.
 	//
 	// The BottomUpMutatorContext provided is for the SDK module.
 	AddDependencies(ctx SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string)
 
-	// Return true if the supplied module is an instance of this member type.
+	// IsInstance returns true if the supplied module is an instance of this member type.
 	//
-	// This is used to check the type of each variant before added to the
-	// SdkMember. Returning false will cause an error to be logged expaining that
-	// the module is not allowed in whichever sdk property it was added.
+	// This is used to check the type of each variant before added to the SdkMember. Returning false
+	// will cause an error to be logged explaining that the module is not allowed in whichever sdk
+	// property it was added.
 	IsInstance(module Module) bool
 
 	// UsesSourceModuleTypeInSnapshot returns true when the AddPrebuiltModule() method returns a
 	// source module type.
 	UsesSourceModuleTypeInSnapshot() bool
 
-	// Add a prebuilt module that the sdk will populate.
+	// AddPrebuiltModule is called to add a prebuilt module that the sdk will populate.
 	//
 	// The sdk module code generates the snapshot as follows:
 	//
@@ -525,7 +538,8 @@
 	//
 	AddPrebuiltModule(ctx SdkMemberContext, member SdkMember) BpModule
 
-	// Create a structure into which variant specific properties can be added.
+	// CreateVariantPropertiesStruct creates a structure into which variant specific properties can be
+	// added.
 	CreateVariantPropertiesStruct() SdkMemberProperties
 }
 
@@ -535,7 +549,8 @@
 	BottomUpMutatorContext
 }
 
-// Base type for SdkMemberType implementations.
+// SdkMemberTypeBase is the base type for SdkMemberType implementations and must be embedded in any
+// struct that implements SdkMemberType.
 type SdkMemberTypeBase struct {
 	PropertyName string
 
@@ -572,7 +587,7 @@
 	return b.UseSourceModuleTypeInSnapshot
 }
 
-// Encapsulates the information about registered SdkMemberTypes.
+// SdkMemberTypesRegistry encapsulates the information about registered SdkMemberTypes.
 type SdkMemberTypesRegistry struct {
 	// The list of types sorted by property name.
 	list []SdkMemberType
@@ -610,14 +625,15 @@
 	return NewCustomOnceKey(r)
 }
 
-// The set of registered SdkMemberTypes for module_exports modules.
+// ModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports modules.
 var ModuleExportsMemberTypes = &SdkMemberTypesRegistry{}
 
-// The set of registered SdkMemberTypes for sdk modules.
+// SdkMemberTypes is the set of registered SdkMemberTypes for sdk modules.
 var SdkMemberTypes = &SdkMemberTypesRegistry{}
 
-// Register an SdkMemberType object to allow them to be used in the sdk and sdk_snapshot module
-// types.
+// RegisterSdkMemberType registers an SdkMemberType object to allow them to be used in the
+// module_exports, module_exports_snapshot and (depending on the value returned from
+// SdkMemberType.UsableWithSdkAndSdkSnapshot) the sdk and sdk_snapshot module types.
 func RegisterSdkMemberType(memberType SdkMemberType) {
 	// All member types are usable with module_exports.
 	ModuleExportsMemberTypes = ModuleExportsMemberTypes.copyAndAppend(memberType)
@@ -628,7 +644,8 @@
 	}
 }
 
-// Base structure for all implementations of SdkMemberProperties.
+// SdkMemberPropertiesBase is the base structure for all implementations of SdkMemberProperties and
+// must be embedded in any struct that implements SdkMemberProperties.
 //
 // Contains common properties that apply across many different member types.
 type SdkMemberPropertiesBase struct {
@@ -655,7 +672,7 @@
 	Compile_multilib string `android:"arch_variant"`
 }
 
-// The os prefix to use for any file paths in the sdk.
+// OsPrefix returns the os prefix to use for any file paths in the sdk.
 //
 // Is an empty string if the member only provides variants for a single os type, otherwise
 // is the OsType.Name.
@@ -671,35 +688,47 @@
 	return b
 }
 
-// Interface to be implemented on top of a structure that contains variant specific
-// information.
+// SdkMemberProperties is the interface to be implemented on top of a structure that contains
+// variant specific information.
 //
-// Struct fields that are capitalized are examined for common values to extract. Fields
-// that are not capitalized are assumed to be arch specific.
+// Struct fields that are capitalized are examined for common values to extract. Fields that are not
+// capitalized are assumed to be arch specific.
 type SdkMemberProperties interface {
-	// Access the base structure.
+	// Base returns the base structure.
 	Base() *SdkMemberPropertiesBase
 
-	// Populate this structure with information from the variant.
+	// PopulateFromVariant populates this structure with information from a module variant.
+	//
+	// It will typically be called once for each variant of a member module that the SDK depends upon.
 	PopulateFromVariant(ctx SdkMemberContext, variant Module)
 
-	// Add the information from this structure to the property set.
+	// AddToPropertySet adds the information from this structure to the property set.
+	//
+	// This will be called for each instance of this structure on which the PopulateFromVariant method
+	// was called and also on a number of different instances of this structure into which properties
+	// common to one or more variants have been copied. Therefore, implementations of this must handle
+	// the case when this structure is only partially populated.
 	AddToPropertySet(ctx SdkMemberContext, propertySet BpPropertySet)
 }
 
-// Provides access to information common to a specific member.
+// SdkMemberContext provides access to information common to a specific member.
 type SdkMemberContext interface {
 
-	// The module context of the sdk common os variant which is creating the snapshot.
+	// SdkModuleContext returns the module context of the sdk common os variant which is creating the
+	// snapshot.
+	//
+	// This is common to all members of the sdk and is not specific to the member being processed.
+	// If information about the member being processed needs to be obtained from this ModuleContext it
+	// must be obtained using one of the OtherModule... methods not the Module... methods.
 	SdkModuleContext() ModuleContext
 
-	// The builder of the snapshot.
+	// SnapshotBuilder the builder of the snapshot.
 	SnapshotBuilder() SnapshotBuilder
 
-	// The type of the member.
+	// MemberType returns the type of the member currently being processed.
 	MemberType() SdkMemberType
 
-	// The name of the member.
+	// Name returns the name of the member currently being processed.
 	//
 	// Provided for use by sdk members to create a member specific location within the snapshot
 	// into which to copy the prebuilt files.
diff --git a/android/testing.go b/android/testing.go
index bd2faa2..7a89fc4 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -491,6 +491,66 @@
 	ctx.preSingletons = append(ctx.preSingletons, newPreSingleton(name, factory))
 }
 
+// ModuleVariantForTests selects a specific variant of the module with the given
+// name by matching the variations map against the variations of each module
+// variant. A module variant matches the map if every variation that exists in
+// both have the same value. Both the module and the map are allowed to have
+// extra variations that the other doesn't have. Panics if not exactly one
+// module variant matches.
+func (ctx *TestContext) ModuleVariantForTests(name string, matchVariations map[string]string) TestingModule {
+	modules := []Module{}
+	ctx.VisitAllModules(func(m blueprint.Module) {
+		if ctx.ModuleName(m) == name {
+			am := m.(Module)
+			amMut := am.base().commonProperties.DebugMutators
+			amVar := am.base().commonProperties.DebugVariations
+			matched := true
+			for i, mut := range amMut {
+				if wantedVar, found := matchVariations[mut]; found && amVar[i] != wantedVar {
+					matched = false
+					break
+				}
+			}
+			if matched {
+				modules = append(modules, am)
+			}
+		}
+	})
+
+	if len(modules) == 0 {
+		// Show all the modules or module variants that do exist.
+		var allModuleNames []string
+		var allVariants []string
+		ctx.VisitAllModules(func(m blueprint.Module) {
+			allModuleNames = append(allModuleNames, ctx.ModuleName(m))
+			if ctx.ModuleName(m) == name {
+				allVariants = append(allVariants, m.(Module).String())
+			}
+		})
+
+		if len(allVariants) == 0 {
+			panic(fmt.Errorf("failed to find module %q. All modules:\n  %s",
+				name, strings.Join(SortedUniqueStrings(allModuleNames), "\n  ")))
+		} else {
+			sort.Strings(allVariants)
+			panic(fmt.Errorf("failed to find module %q matching %v. All variants:\n  %s",
+				name, matchVariations, strings.Join(allVariants, "\n  ")))
+		}
+	}
+
+	if len(modules) > 1 {
+		moduleStrings := []string{}
+		for _, m := range modules {
+			moduleStrings = append(moduleStrings, m.String())
+		}
+		sort.Strings(moduleStrings)
+		panic(fmt.Errorf("module %q has more than one variant that match %v:\n  %s",
+			name, matchVariations, strings.Join(moduleStrings, "\n  ")))
+	}
+
+	return newTestingModule(ctx.config, modules[0])
+}
+
 func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {
 	var module Module
 	ctx.VisitAllModules(func(m blueprint.Module) {
diff --git a/android/variable.go b/android/variable.go
index a1af527..a308d2b 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -333,6 +333,7 @@
 	VendorSnapshotDirsExcluded   []string `json:",omitempty"`
 	RecoverySnapshotDirsExcluded []string `json:",omitempty"`
 	RecoverySnapshotDirsIncluded []string `json:",omitempty"`
+	HostFakeSnapshotEnabled      bool     `json:",omitempty"`
 
 	BoardVendorSepolicyDirs      []string `json:",omitempty"`
 	BoardOdmSepolicyDirs         []string `json:",omitempty"`
diff --git a/apex/apex.go b/apex/apex.go
index e3edc68..2d153e2 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1569,6 +1569,11 @@
 	af.jacocoReportClassesFile = module.JacocoReportClassesFile()
 	af.lintDepSets = module.LintDepSets()
 	af.customStem = module.Stem() + ".jar"
+	if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
+		for _, install := range dexpreopter.DexpreoptBuiltInstalledForApex() {
+			af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
+		}
+	}
 	return af
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 6027f9b..2a2a1f4 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -932,9 +932,17 @@
 	// .. and not linking to the stubs variant of mylib3
 	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12/mylib3.so")
 
+	// Comment out this test. Now it fails after the optimization of sharing "cflags" in cc/cc.go
+	// is replaced by sharing of "cFlags" in cc/builder.go.
+	// The "cflags" contains "-include mylib.h", but cFlags contained only a reference to the
+	// module variable representing "cflags". So it was not detected by ensureNotContains.
+	// Now "cFlags" is a reference to a module variable like $flags1, which includes all previous
+	// content of "cflags". ModuleForTests...Args["cFlags"] returns the full string of $flags1,
+	// including the original cflags's "-include mylib.h".
+	//
 	// Ensure that stubs libs are built without -include flags
-	mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
-	ensureNotContains(t, mylib2Cflags, "-include ")
+	// mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	// ensureNotContains(t, mylib2Cflags, "-include ")
 
 	// Ensure that genstub is invoked with --apex
 	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"])
@@ -2179,6 +2187,7 @@
 		name          string
 		expectedError string
 		bp            string
+		preparer      android.FixturePreparer
 	}{
 		{
 			name: "Non-updatable apex with non-stable dep",
@@ -2250,6 +2259,30 @@
 			`,
 		},
 		{
+			name:          "Updatable apex with non-stable legacy core platform dep",
+			expectedError: `\Qcannot depend on "myjar-uses-legacy": non stable SDK core_platform_current - uses legacy core platform\E`,
+			bp: `
+				apex {
+					name: "myapex",
+					java_libs: ["myjar-uses-legacy"],
+					key: "myapex.key",
+					updatable: true,
+				}
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+				java_library {
+					name: "myjar-uses-legacy",
+					srcs: ["foo/bar/MyClass.java"],
+					sdk_version: "core_platform",
+					apex_available: ["myapex"],
+				}
+			`,
+			preparer: java.FixtureUseLegacyCorePlatformApi("myjar-uses-legacy"),
+		},
+		{
 			name: "Updatable apex with non-stable transitive dep",
 			// This is not actually detecting that the transitive dependency is unstable, rather it is
 			// detecting that the transitive dependency is building against a wider API surface than the
@@ -2285,12 +2318,22 @@
 	}
 
 	for _, test := range testCases {
+		if test.name != "Updatable apex with non-stable legacy core platform dep" {
+			continue
+		}
 		t.Run(test.name, func(t *testing.T) {
-			if test.expectedError == "" {
-				testApex(t, test.bp)
-			} else {
-				testApexError(t, test.expectedError, test.bp)
+			errorHandler := android.FixtureExpectsNoErrors
+			if test.expectedError != "" {
+				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
 			}
+			android.GroupFixturePreparers(
+				java.PrepareForTestWithJavaDefaultModules,
+				PrepareForTestWithApexBuildComponents,
+				prepareForTestWithMyapex,
+				android.OptionalFixturePreparer(test.preparer),
+			).
+				ExtendWithErrorHandler(errorHandler).
+				RunTestWithBp(t, test.bp)
 		})
 	}
 }
diff --git a/apex/builder.go b/apex/builder.go
index 3177ee0..6df40f4 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -17,7 +17,6 @@
 import (
 	"encoding/json"
 	"fmt"
-	"path"
 	"path/filepath"
 	"runtime"
 	"sort"
@@ -256,14 +255,24 @@
 // labeled as system_file.
 func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.OutputPath {
 	var fileContexts android.Path
+	var fileContextsDir string
 	if a.properties.File_contexts == nil {
 		fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts")
 	} else {
+		if m, t := android.SrcIsModuleWithTag(*a.properties.File_contexts); m != "" {
+			otherModule := android.GetModuleFromPathDep(ctx, m, t)
+			fileContextsDir = ctx.OtherModuleDir(otherModule)
+		}
 		fileContexts = android.PathForModuleSrc(ctx, *a.properties.File_contexts)
 	}
+	if fileContextsDir == "" {
+		fileContextsDir = filepath.Dir(fileContexts.String())
+	}
+	fileContextsDir += string(filepath.Separator)
+
 	if a.Platform() {
-		if matched, err := path.Match("system/sepolicy/**/*", fileContexts.String()); err != nil || !matched {
-			ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but %q", fileContexts)
+		if !strings.HasPrefix(fileContextsDir, "system/sepolicy/") {
+			ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but found in  %q", fileContextsDir)
 		}
 	}
 	if !android.ExistentPathForSource(ctx, fileContexts.String()).Valid() {
diff --git a/apex/deapexer.go b/apex/deapexer.go
index c70da15..2c1835a 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -97,7 +97,7 @@
 	// Create and remember the directory into which the .apex file's contents will be unpacked.
 	deapexerOutput := android.PathForModuleOut(ctx, "deapexer")
 
-	exports := make(map[string]android.Path)
+	exports := make(map[string]android.WritablePath)
 
 	// Create mappings from apex relative path to the extracted file's path.
 	exportedPaths := make(android.Paths, 0, len(exports))
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 513ddc0..d8a4a7a 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -21,6 +21,7 @@
 
 	"android/soong/android"
 	"android/soong/java"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -325,31 +326,15 @@
 }
 
 // TestPlatformBootclasspath_AlwaysUsePrebuiltSdks verifies that the build does not fail when
-// AlwaysUsePrebuiltSdk() returns true. The structure of the modules in this test matches what
-// currently exists in some places in the Android build but it is not the intended structure. It is
-// in fact an invalid structure that should cause build failures. However, fixing that structure
-// will take too long so in the meantime this tests the workarounds to avoid build breakages.
-//
-// The main issues with this structure are:
-// 1. There is no prebuilt_bootclasspath_fragment referencing the "foo" java_sdk_library_import.
-// 2. There is no prebuilt_apex/apex_set which makes the dex implementation jar available to the
-//    prebuilt_bootclasspath_fragment and the "foo" java_sdk_library_import.
-//
-// Together these cause the following symptoms:
-// 1. The "foo" java_sdk_library_import does not have a dex implementation jar.
-// 2. The "foo" java_sdk_library_import does not have a myapex variant.
-//
-// TODO(b/179354495): Fix the structure in this test once the main Android build has been fixed.
+// AlwaysUsePrebuiltSdk() returns true.
 func TestPlatformBootclasspath_AlwaysUsePrebuiltSdks(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		prepareForTestWithMyapex,
 		// Configure two libraries, the first is a java_sdk_library whose prebuilt will be used because
-		// of AlwaysUsePrebuiltsSdk() but does not have an appropriate apex variant and does not provide
-		// a boot dex jar. The second is a normal library that is unaffected. The order matters because
-		// if the dependency on myapex:foo is filtered out because of either of those conditions then
-		// the dependencies resolved by the platform_bootclasspath will not match the configured list
-		// and so will fail the test.
+		// of AlwaysUsePrebuiltsSdk(). The second is a normal library that is unaffected. The order
+		// matters, so that the dependencies resolved by the platform_bootclasspath matches the
+		// configured list.
 		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -394,6 +379,12 @@
 			permitted_packages: ["foo"],
 		}
 
+		prebuilt_apex {
+			name: "myapex",
+			src: "myapex.apex",
+			exported_bootclasspath_fragments: ["mybootclasspath-fragment"],
+		}
+
 		// A prebuilt java_sdk_library_import that is not preferred by default but will be preferred
 		// because AlwaysUsePrebuiltSdks() is true.
 		java_sdk_library_import {
@@ -423,6 +414,23 @@
 			],
 		}
 
+		prebuilt_bootclasspath_fragment {
+			name: "mybootclasspath-fragment",
+			apex_available: [
+				"myapex",
+			],
+			contents: [
+				"foo",
+			],
+			hidden_api: {
+				stub_flags: "",
+				annotation_flags: "",
+				metadata: "",
+				index: "",
+				all_flags: "",
+			},
+		}
+
 		platform_bootclasspath {
 			name: "myplatform-bootclasspath",
 			fragments: [
@@ -437,7 +445,7 @@
 
 	java.CheckPlatformBootclasspathModules(t, result, "myplatform-bootclasspath", []string{
 		// The configured contents of BootJars.
-		"platform:prebuilt_foo", // Note: This is the platform not myapex variant.
+		"myapex:prebuilt_foo",
 		"myapex:bar",
 	})
 
@@ -456,16 +464,15 @@
 
 		// The platform_bootclasspath intentionally adds dependencies on both source and prebuilt
 		// modules when available as it does not know which one will be preferred.
-		//
-		// The source module has an APEX variant but the prebuilt does not.
 		"myapex:foo",
-		"platform:prebuilt_foo",
+		"myapex:prebuilt_foo",
 
 		// Only a source module exists.
 		"myapex:bar",
 
 		// The fragments.
 		"myapex:mybootclasspath-fragment",
+		"myapex:prebuilt_mybootclasspath-fragment",
 	})
 }
 
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index c4794dc..4833a64 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -178,13 +178,19 @@
 			// If the exported java module provides a dex jar path then add it to the list of apexFiles.
 			path := child.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
 			if path != nil {
-				p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, apexFile{
+				af := apexFile{
 					module:              child,
 					moduleDir:           ctx.OtherModuleDir(child),
 					androidMkModuleName: name,
 					builtFile:           path,
 					class:               javaSharedLib,
-				})
+				}
+				if module, ok := child.(java.DexpreopterInterface); ok {
+					for _, install := range module.DexpreoptBuiltInstalledForApex() {
+						af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
+					}
+				}
+				p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, af)
 			}
 		} else if tag == exportedBootclasspathFragmentTag {
 			// Visit the children of the bootclasspath_fragment.
@@ -195,6 +201,14 @@
 	})
 }
 
+func (p *prebuiltCommon) addRequiredModules(entries *android.AndroidMkEntries) {
+	for _, fi := range p.apexFilesForAndroidMk {
+		entries.AddStrings("LOCAL_REQUIRED_MODULES", fi.requiredModuleNames...)
+		entries.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", fi.targetRequiredModuleNames...)
+		entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", fi.hostRequiredModuleNames...)
+	}
+}
+
 func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := []android.AndroidMkEntries{
 		{
@@ -213,6 +227,7 @@
 					if len(postInstallCommands) > 0 {
 						entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(postInstallCommands, " && "))
 					}
+					p.addRequiredModules(entries)
 				},
 			},
 		},
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 131f0ec..d731f3e 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -25,6 +25,7 @@
 	// be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles,
 	// but general cc_library will also have dynamic libraries in output files).
 	RootDynamicLibraries []string
+	TocFile              string
 }
 
 type getOutputFilesRequestType struct{}
@@ -100,14 +101,15 @@
 func (g getCcInfoType) StarlarkFunctionBody() string {
 	return `
 outputFiles = [f.path for f in target.files.to_list()]
+cc_info = providers(target)["CcInfo"]
 
-includes = providers(target)["CcInfo"].compilation_context.includes.to_list()
-system_includes = providers(target)["CcInfo"].compilation_context.system_includes.to_list()
+includes = cc_info.compilation_context.includes.to_list()
+system_includes = cc_info.compilation_context.system_includes.to_list()
 
 ccObjectFiles = []
 staticLibraries = []
 rootStaticArchives = []
-linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
+linker_inputs = cc_info.linking_context.linker_inputs.to_list()
 
 for linker_input in linker_inputs:
   for library in linker_input.libraries:
@@ -120,11 +122,17 @@
 
 rootDynamicLibraries = []
 
-if "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo" in providers(target):
-  shared_info = providers(target)["@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"]
+shared_info_tag = "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"
+if shared_info_tag in providers(target):
+  shared_info = providers(target)[shared_info_tag]
   for lib in shared_info.linker_input.libraries:
     rootDynamicLibraries += [lib.dynamic_library.path]
 
+toc_file = ""
+toc_file_tag = "//build/bazel/rules:generate_toc.bzl%CcTocInfo"
+if toc_file_tag in providers(target):
+  toc_file = providers(target)[toc_file_tag].toc.path
+
 returns = [
   outputFiles,
   staticLibraries,
@@ -132,10 +140,10 @@
   includes,
   system_includes,
   rootStaticArchives,
-  rootDynamicLibraries
+  rootDynamicLibraries,
 ]
 
-return "|".join([", ".join(r) for r in returns])`
+return "|".join([", ".join(r) for r in returns] + [toc_file])`
 }
 
 // ParseResult returns a value obtained by parsing the result of the request's Starlark function.
@@ -146,7 +154,7 @@
 	var ccObjects []string
 
 	splitString := strings.Split(rawString, "|")
-	if expectedLen := 7; len(splitString) != expectedLen {
+	if expectedLen := 8; len(splitString) != expectedLen {
 		return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
 	}
 	outputFilesString := splitString[0]
@@ -159,6 +167,7 @@
 	systemIncludes := splitOrEmpty(splitString[4], ", ")
 	rootStaticArchives := splitOrEmpty(splitString[5], ", ")
 	rootDynamicLibraries := splitOrEmpty(splitString[6], ", ")
+	tocFile := splitString[7] // NOTE: Will be the empty string if there wasn't
 	return CcInfo{
 		OutputFiles:          outputFiles,
 		CcObjectFiles:        ccObjects,
@@ -167,6 +176,7 @@
 		SystemIncludes:       systemIncludes,
 		RootStaticArchives:   rootStaticArchives,
 		RootDynamicLibraries: rootDynamicLibraries,
+		TocFile:              tocFile,
 	}, nil
 }
 
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 49019ab..34d0832 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -71,7 +71,7 @@
 	}{
 		{
 			description: "no result",
-			input:       "||||||",
+			input:       "|||||||",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{},
 				CcObjectFiles:        []string{},
@@ -80,11 +80,12 @@
 				SystemIncludes:       []string{},
 				RootStaticArchives:   []string{},
 				RootDynamicLibraries: []string{},
+				TocFile:              "",
 			},
 		},
 		{
 			description: "only output",
-			input:       "test||||||",
+			input:       "test|||||||",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{"test"},
 				CcObjectFiles:        []string{},
@@ -93,11 +94,12 @@
 				SystemIncludes:       []string{},
 				RootStaticArchives:   []string{},
 				RootDynamicLibraries: []string{},
+				TocFile:              "",
 			},
 		},
 		{
 			description: "all items set",
-			input:       "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1",
+			input:       "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1|lib.so.toc",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{"out1", "out2"},
 				CcObjectFiles:        []string{"object1", "object2"},
@@ -106,19 +108,20 @@
 				SystemIncludes:       []string{"system/dir", "system/other/dir"},
 				RootStaticArchives:   []string{"rootstaticarchive1"},
 				RootDynamicLibraries: []string{"rootdynamiclibrary1"},
+				TocFile:              "lib.so.toc",
 			},
 		},
 		{
 			description:          "too few result splits",
 			input:                "|",
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, []string{"", ""}),
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 8, []string{"", ""}),
 		},
 		{
 			description:          "too many result splits",
 			input:                strings.Repeat("|", 8),
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, make([]string, 9)),
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 8, make([]string, 9)),
 		},
 	}
 	for _, tc := range testCases {
diff --git a/bazel/properties.go b/bazel/properties.go
index ed4e9fc..d3b40a2 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -19,7 +19,6 @@
 	"path/filepath"
 	"regexp"
 	"sort"
-	"strings"
 )
 
 // BazelTargetModuleProperties contain properties and metadata used for
@@ -32,12 +31,6 @@
 	Bzl_load_location string `blueprint:"mutated"`
 }
 
-const BazelTargetModuleNamePrefix = "__bp2build__"
-
-func StripNamePrefix(moduleName string) string {
-	return strings.TrimPrefix(moduleName, BazelTargetModuleNamePrefix)
-}
-
 var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
 
 // Label is used to represent a Bazel compatible Label. Also stores the original
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 5ee04f9..40526a6 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -11,7 +11,6 @@
         "build_conversion.go",
         "bzl_conversion.go",
         "configurability.go",
-        "compatibility.go",
         "constants.go",
         "conversion.go",
         "metrics.go",
@@ -37,6 +36,7 @@
         "cc_library_conversion_test.go",
         "cc_library_headers_conversion_test.go",
         "cc_library_static_conversion_test.go",
+        "cc_library_shared_conversion_test.go",
         "cc_object_conversion_test.go",
         "conversion_test.go",
         "filegroup_conversion_test.go",
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 06a7306..45a3cb6 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -19,6 +19,7 @@
 	"android/soong/bazel"
 	"fmt"
 	"os"
+	"strings"
 )
 
 // Codegen is the backend of bp2build. The code generator is responsible for
@@ -29,14 +30,22 @@
 	bp2buildDir := android.PathForOutput(ctx, "bp2build")
 	android.RemoveAllOutputDir(bp2buildDir)
 
-	buildToTargets, metrics, compatLayer := GenerateBazelTargets(ctx, true)
-	bp2buildFiles := CreateBazelFiles(nil, buildToTargets, ctx.mode)
+	res, errs := GenerateBazelTargets(ctx, true)
+	if len(errs) > 0 {
+		errMsgs := make([]string, len(errs))
+		for i, err := range errs {
+			errMsgs[i] = fmt.Sprintf("%q", err)
+		}
+		fmt.Printf("ERROR: Encountered %d error(s): \nERROR: %s", len(errs), strings.Join(errMsgs, "\n"))
+		os.Exit(1)
+	}
+	bp2buildFiles := CreateBazelFiles(nil, res.buildFileToTargets, ctx.mode)
 	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 
 	soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
-	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(compatLayer))
+	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(res.metrics))
 
-	return metrics
+	return res.metrics
 }
 
 // Get the output directory and create it if it doesn't exist.
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index f652a35..f7b392b 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -153,10 +153,11 @@
 }
 
 type CodegenContext struct {
-	config         android.Config
-	context        android.Context
-	mode           CodegenMode
-	additionalDeps []string
+	config             android.Config
+	context            android.Context
+	mode               CodegenMode
+	additionalDeps     []string
+	unconvertedDepMode unconvertedDepsMode
 }
 
 func (c *CodegenContext) Mode() CodegenMode {
@@ -181,6 +182,16 @@
 	QueryView
 )
 
+type unconvertedDepsMode int
+
+const (
+	// Include a warning in conversion metrics about converted modules with unconverted direct deps
+	warnUnconvertedDeps unconvertedDepsMode = iota
+	// Error and fail conversion if encountering a module with unconverted direct deps
+	// Enabled by setting environment variable `BP2BUILD_ERROR_UNCONVERTED`
+	errorModulesUnconvertedDeps
+)
+
 func (mode CodegenMode) String() string {
 	switch mode {
 	case Bp2Build:
@@ -211,10 +222,15 @@
 // NewCodegenContext creates a wrapper context that conforms to PathContext for
 // writing BUILD files in the output directory.
 func NewCodegenContext(config android.Config, context android.Context, mode CodegenMode) *CodegenContext {
+	var unconvertedDeps unconvertedDepsMode
+	if config.IsEnvTrue("BP2BUILD_ERROR_UNCONVERTED") {
+		unconvertedDeps = errorModulesUnconvertedDeps
+	}
 	return &CodegenContext{
-		context: context,
-		config:  config,
-		mode:    mode,
+		context:            context,
+		config:             config,
+		mode:               mode,
+		unconvertedDepMode: unconvertedDeps,
 	}
 }
 
@@ -230,7 +246,16 @@
 	return attributes
 }
 
-func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[string]BazelTargets, CodegenMetrics, CodegenCompatLayer) {
+type conversionResults struct {
+	buildFileToTargets map[string]BazelTargets
+	metrics            CodegenMetrics
+}
+
+func (r conversionResults) BuildDirToTargets() map[string]BazelTargets {
+	return r.buildFileToTargets
+}
+
+func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) {
 	buildFileToTargets := make(map[string]BazelTargets)
 	buildFileToAppend := make(map[string]bool)
 
@@ -239,12 +264,10 @@
 		RuleClassCount: make(map[string]int),
 	}
 
-	compatLayer := CodegenCompatLayer{
-		NameToLabelMap: make(map[string]string),
-	}
-
 	dirs := make(map[string]bool)
 
+	var errs []error
+
 	bpCtx := ctx.Context()
 	bpCtx.VisitAllModules(func(m blueprint.Module) {
 		dir := bpCtx.ModuleDir(m)
@@ -257,7 +280,7 @@
 			if b, ok := m.(android.Bazelable); ok && b.HasHandcraftedLabel() {
 				metrics.handCraftedTargetCount += 1
 				metrics.TotalModuleCount += 1
-				compatLayer.AddNameToLabelEntry(m.Name(), b.HandcraftedLabel())
+				metrics.AddConvertedModule(m.Name())
 				pathToBuildFile := getBazelPackagePath(b)
 				// We are using the entire contents of handcrafted build file, so if multiple targets within
 				// a package have handcrafted targets, we only want to include the contents one time.
@@ -266,19 +289,28 @@
 				}
 				t, err := getHandcraftedBuildContent(ctx, b, pathToBuildFile)
 				if err != nil {
-					panic(fmt.Errorf("Error converting %s: %s", bpCtx.ModuleName(m), err))
+					errs = append(errs, fmt.Errorf("Error converting %s: %s", bpCtx.ModuleName(m), err))
+					return
 				}
 				targets = append(targets, t)
 				// TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
 				// something more targeted based on the rule type and target
 				buildFileToAppend[pathToBuildFile] = true
 			} else if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
+				if unconvertedDeps := aModule.GetUnconvertedBp2buildDeps(); len(unconvertedDeps) > 0 {
+					msg := fmt.Sprintf("%q depends on unconverted modules: %s", m.Name(), strings.Join(unconvertedDeps, ", "))
+					if ctx.unconvertedDepMode == warnUnconvertedDeps {
+						metrics.moduleWithUnconvertedDepsMsgs = append(metrics.moduleWithUnconvertedDepsMsgs, msg)
+					} else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps {
+						metrics.TotalModuleCount += 1
+						errs = append(errs, fmt.Errorf(msg))
+						return
+					}
+				}
 				targets = generateBazelTargets(bpCtx, aModule)
 				for _, t := range targets {
-					if t.name == m.Name() {
-						// only add targets that exist in Soong to compatibility layer
-						compatLayer.AddNameToLabelEntry(m.Name(), t.Label())
-					}
+					// only add targets that exist in Soong to compatibility layer
+					metrics.AddConvertedModule(m.Name())
 					metrics.RuleClassCount[t.ruleClass] += 1
 				}
 			} else {
@@ -295,11 +327,17 @@
 			t := generateSoongModuleTarget(bpCtx, m)
 			targets = append(targets, t)
 		default:
-			panic(fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
+			errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
+			return
 		}
 
 		buildFileToTargets[dir] = append(buildFileToTargets[dir], targets...)
 	})
+
+	if len(errs) > 0 {
+		return conversionResults{}, errs
+	}
+
 	if generateFilegroups {
 		// Add a filegroup target that exposes all sources in the subtree of this package
 		// NOTE: This also means we generate a BUILD file for every Android.bp file (as long as it has at least one module)
@@ -312,7 +350,10 @@
 		}
 	}
 
-	return buildFileToTargets, metrics, compatLayer
+	return conversionResults{
+		buildFileToTargets: buildFileToTargets,
+		metrics:            metrics,
+	}, errs
 }
 
 func getBazelPackagePath(b android.Bazelable) string {
@@ -575,6 +616,19 @@
 			// Ignore zero-valued fields
 			continue
 		}
+		// if the struct is embedded (anonymous), flatten the properties into the containing struct
+		if field.Anonymous {
+			if field.Type.Kind() == reflect.Ptr {
+				fieldValue = fieldValue.Elem()
+			}
+			if fieldValue.Type().Kind() == reflect.Struct {
+				propsToMerge := extractStructProperties(fieldValue, indent)
+				for prop, value := range propsToMerge {
+					ret[prop] = value
+				}
+				continue
+			}
+		}
 
 		propertyName := proptools.PropertyNameForField(field.Name)
 		prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
@@ -648,10 +702,6 @@
 	return strings.Repeat("    ", indent)
 }
 
-func targetNameForBp2Build(c bpToBuildContext, logicModule blueprint.Module) string {
-	return strings.Replace(c.ModuleName(logicModule), bazel.BazelTargetModuleNamePrefix, "", 1)
-}
-
 func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
 	name := ""
 	if c.ModuleSubDir(logicModule) != "" {
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index ecea6b2..e904627 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"fmt"
 	"strings"
 	"testing"
 )
@@ -199,7 +200,8 @@
 			android.FailIfErrored(t, errs)
 
 			codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView)
-			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+			android.FailIfErrored(t, err)
 			if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
 				t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
 			}
@@ -322,6 +324,30 @@
 )`,
 			},
 		},
+		{
+			blueprint: `custom {
+    name: "embedded_props",
+    embedded_prop: "abc",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`custom(
+    name = "embedded_props",
+    embedded_attr = "abc",
+)`,
+			},
+		},
+		{
+			blueprint: `custom {
+    name: "ptr_to_embedded_props",
+    other_embedded_prop: "abc",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`custom(
+    name = "ptr_to_embedded_props",
+    other_embedded_attr = "abc",
+)`,
+			},
+		},
 	}
 
 	dir := "."
@@ -341,7 +367,8 @@
 		}
 
 		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+		android.FailIfErrored(t, err)
 
 		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
 			t.Errorf("Expected %d bazel target, got %d", expectedCount, actualCount)
@@ -502,7 +529,8 @@
 		android.FailIfErrored(t, errs)
 
 		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+		android.FailIfErrored(t, err)
 		if actualCount := len(bazelTargets); actualCount != testCase.expectedBazelTargetCount {
 			t.Fatalf("Expected %d bazel target, got %d", testCase.expectedBazelTargetCount, actualCount)
 		}
@@ -679,59 +707,38 @@
 				"other/Android.bp": `filegroup {
     name: "foo",
     srcs: ["a", "b"],
+    bazel_module: { bp2build_available: true },
+}`,
+			},
+		},
+		{
+			description:                        "depends_on_other_unconverted_module_error",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			unconvertedDepsMode:                errorModulesUnconvertedDeps,
+			blueprint: `filegroup {
+    name: "foobar",
+    srcs: [
+        ":foo",
+        "c",
+    ],
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedErr: fmt.Errorf(`"foobar" depends on unconverted modules: foo`),
+			filesystem: map[string]string{
+				"other/Android.bp": `filegroup {
+    name: "foo",
+    srcs: ["a", "b"],
 }`,
 			},
 		},
 	}
 
-	dir := "."
 	for _, testCase := range testCases {
-		fs := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		for f, content := range testCase.filesystem {
-			if strings.HasSuffix(f, "Android.bp") {
-				toParse = append(toParse, f)
-			}
-			fs[f] = []byte(content)
-		}
-		config := android.TestConfig(buildDir, nil, testCase.blueprint, fs)
-		ctx := android.NewTestContext(config)
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
-
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if errored(t, testCase, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if errored(t, testCase, errs) {
-			continue
-		}
-
-		checkDir := dir
-		if testCase.dir != "" {
-			checkDir = testCase.dir
-		}
-
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
-		} else {
-			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
-					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
-					)
-				}
-			}
-		}
+		t.Run(testCase.description, func(t *testing.T) {
+			runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, testCase)
+		})
 	}
 }
 
@@ -809,7 +816,8 @@
 			android.FailIfErrored(t, errs)
 
 			codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+			android.FailIfErrored(t, err)
 			if actualCount := len(bazelTargets); actualCount != testCase.expectedCount {
 				t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, testCase.expectedCount, actualCount)
 			}
@@ -921,7 +929,8 @@
 
 		// For each directory, test that the expected number of generated targets is correct.
 		for dir, expectedCount := range testCase.expectedCount {
-			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+			android.FailIfErrored(t, err)
 			if actualCount := len(bazelTargets); actualCount != expectedCount {
 				t.Fatalf(
 					"%s: Expected %d bazel target for %s package, got %d",
@@ -1064,7 +1073,9 @@
 			if testCase.dir != "" {
 				checkDir = testCase.dir
 			}
-			bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
+			codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
+			android.FailIfErrored(t, err)
 			bazelTargets.sort()
 			actualCount := len(bazelTargets)
 			expectedCount := len(testCase.expectedBazelTargets)
@@ -1185,7 +1196,9 @@
 		if testCase.dir != "" {
 			checkDir = testCase.dir
 		}
-		bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
+		android.FailIfErrored(t, err)
 		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
 			t.Errorf("%s: Expected %d bazel target, got %d\n%s", testCase.description, expectedCount, actualCount, bazelTargets)
 		} else {
diff --git a/bp2build/bzl_conversion.go b/bp2build/bzl_conversion.go
index f2f6b01..992cc1c 100644
--- a/bp2build/bzl_conversion.go
+++ b/bp2build/bzl_conversion.go
@@ -160,8 +160,15 @@
 		if shouldSkipStructField(field) {
 			continue
 		}
-
-		properties = append(properties, extractPropertyDescriptions(field.Name, field.Type)...)
+		subProps := extractPropertyDescriptions(field.Name, field.Type)
+		// if the struct is embedded (anonymous), flatten the properties into the containing struct
+		if field.Anonymous {
+			for _, prop := range subProps {
+				properties = append(properties, prop.properties...)
+			}
+		} else {
+			properties = append(properties, subProps...)
+		}
 	}
 	return properties
 }
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 9e0c0a1..1e78c0e 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -92,6 +92,7 @@
         # bazel_module end
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
+        "embedded_prop": attr.string(),
         "int64_ptr_prop": attr.int(),
         # nested_props start
 #         "nested_prop": attr.string(),
@@ -99,6 +100,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
@@ -118,6 +120,7 @@
         "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
+        "embedded_prop": attr.string(),
         "int64_ptr_prop": attr.int(),
         # nested_props start
 #         "nested_prop": attr.string(),
@@ -125,6 +128,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
@@ -144,6 +148,7 @@
         "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
+        "embedded_prop": attr.string(),
         "int64_ptr_prop": attr.int(),
         # nested_props start
 #         "nested_prop": attr.string(),
@@ -151,6 +156,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index c2c35e7..371593b 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -255,13 +255,11 @@
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "fake-libarm-optimized-routines-math",
-    copts = [
-        "-Iexternal",
-        "-I$(BINDIR)/external",
-    ] + select({
+    copts = select({
         "//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"],
         "//conditions:default": [],
     }),
+    local_includes = ["."],
     srcs_c = ["math/cosf.c"],
 )`},
 	})
@@ -494,12 +492,9 @@
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = [
-        "bothflag",
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
+    copts = ["bothflag"],
     implementation_deps = [":static_dep_for_both"],
+    local_includes = ["."],
     shared = {
         "copts": ["sharedflag"] + select({
             "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
@@ -635,14 +630,7 @@
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    asflags = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
+    local_includes = ["."],
     shared = {
         "srcs": [
             ":shared_filegroup_cpp_srcs",
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 8b490f8..37d806c 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -130,10 +130,6 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_headers(
     name = "foo_headers",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     export_includes = [
         "dir-1",
         "dir-2",
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
new file mode 100644
index 0000000..52a07cc
--- /dev/null
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -0,0 +1,366 @@
+// 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 (
+	"testing"
+
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+const (
+	// See cc/testing.go for more context
+	// TODO(alexmarquez): Split out the preamble into common code?
+	soongCcLibrarySharedPreamble = soongCcLibraryStaticPreamble
+)
+
+func registerCcLibrarySharedModuleTypes(ctx android.RegistrationContext) {
+	cc.RegisterCCBuildComponents(ctx)
+	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
+}
+
+func runCcLibrarySharedTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerCcLibrarySharedModuleTypes, tc)
+}
+
+func TestCcLibrarySharedSimple(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared simple overall test",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem: map[string]string{
+			// NOTE: include_dir headers *should not* appear in Bazel hdrs later (?)
+			"include_dir_1/include_dir_1_a.h": "",
+			"include_dir_1/include_dir_1_b.h": "",
+			"include_dir_2/include_dir_2_a.h": "",
+			"include_dir_2/include_dir_2_b.h": "",
+			// NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?)
+			"local_include_dir_1/local_include_dir_1_a.h": "",
+			"local_include_dir_1/local_include_dir_1_b.h": "",
+			"local_include_dir_2/local_include_dir_2_a.h": "",
+			"local_include_dir_2/local_include_dir_2_b.h": "",
+			// NOTE: export_include_dir headers *should* appear in Bazel hdrs later
+			"export_include_dir_1/export_include_dir_1_a.h": "",
+			"export_include_dir_1/export_include_dir_1_b.h": "",
+			"export_include_dir_2/export_include_dir_2_a.h": "",
+			"export_include_dir_2/export_include_dir_2_b.h": "",
+			// NOTE: Soong implicitly includes headers in the current directory
+			"implicit_include_1.h": "",
+			"implicit_include_2.h": "",
+		},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_headers {
+    name: "header_lib_1",
+    export_include_dirs: ["header_lib_1"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_headers {
+    name: "header_lib_2",
+    export_include_dirs: ["header_lib_2"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_shared {
+    name: "shared_lib_1",
+    srcs: ["shared_lib_1.cc"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_shared {
+    name: "shared_lib_2",
+    srcs: ["shared_lib_2.cc"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+    name: "whole_static_lib_1",
+    srcs: ["whole_static_lib_1.cc"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+    name: "whole_static_lib_2",
+    srcs: ["whole_static_lib_2.cc"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_shared {
+    name: "foo_shared",
+    srcs: [
+        "foo_shared1.cc",
+        "foo_shared2.cc",
+    ],
+    cflags: [
+        "-Dflag1",
+        "-Dflag2"
+    ],
+    shared_libs: [
+        "shared_lib_1",
+        "shared_lib_2"
+    ],
+    whole_static_libs: [
+        "whole_static_lib_1",
+        "whole_static_lib_2"
+    ],
+    include_dirs: [
+        "include_dir_1",
+        "include_dir_2",
+    ],
+    local_include_dirs: [
+        "local_include_dir_1",
+        "local_include_dir_2",
+    ],
+    export_include_dirs: [
+        "export_include_dir_1",
+        "export_include_dir_2"
+    ],
+    header_libs: [
+        "header_lib_1",
+        "header_lib_2"
+    ],
+
+    // TODO: Also support export_header_lib_headers
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    absolute_includes = [
+        "include_dir_1",
+        "include_dir_2",
+    ],
+    copts = [
+        "-Dflag1",
+        "-Dflag2",
+    ],
+    dynamic_deps = [
+        ":shared_lib_1",
+        ":shared_lib_2",
+    ],
+    export_includes = [
+        "export_include_dir_1",
+        "export_include_dir_2",
+    ],
+    implementation_deps = [
+        ":header_lib_1",
+        ":header_lib_2",
+    ],
+    local_includes = [
+        "local_include_dir_1",
+        "local_include_dir_2",
+        ".",
+    ],
+    srcs = [
+        "foo_shared1.cc",
+        "foo_shared2.cc",
+    ],
+    whole_archive_deps = [
+        ":whole_static_lib_1",
+        ":whole_static_lib_2",
+    ],
+)`},
+	})
+}
+
+func TestCcLibrarySharedArchSpecificSharedLib(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared arch-specific shared_libs with whole_static_libs",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_static {
+    name: "static_dep",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "shared_dep",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "foo_shared",
+    arch: { arm64: { shared_libs: ["shared_dep"], whole_static_libs: ["static_dep"] } },
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    dynamic_deps = select({
+        "//build/bazel/platforms/arch:arm64": [":shared_dep"],
+        "//conditions:default": [],
+    }),
+    whole_archive_deps = select({
+        "//build/bazel/platforms/arch:arm64": [":static_dep"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
+}
+
+func TestCcLibrarySharedOsSpecificSharedLib(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared os-specific shared_libs",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "shared_dep",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "foo_shared",
+    target: { android: { shared_libs: ["shared_dep"], } },
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    dynamic_deps = select({
+        "//build/bazel/platforms/os:android": [":shared_dep"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
+}
+
+func TestCcLibrarySharedBaseArchOsSpecificSharedLib(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared base, arch, and os-specific shared_libs",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "shared_dep",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "shared_dep2",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "shared_dep3",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "foo_shared",
+    shared_libs: ["shared_dep"],
+    target: { android: { shared_libs: ["shared_dep2"] } },
+    arch: { arm64: { shared_libs: ["shared_dep3"] } },
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    dynamic_deps = [":shared_dep"] + select({
+        "//build/bazel/platforms/arch:arm64": [":shared_dep3"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:android": [":shared_dep2"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
+}
+
+func TestCcLibrarySharedSimpleExcludeSrcs(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared simple exclude_srcs",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem: map[string]string{
+			"common.c":       "",
+			"foo-a.c":        "",
+			"foo-excluded.c": "",
+		},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    srcs: ["common.c", "foo-*.c"],
+    exclude_srcs: ["foo-excluded.c"],
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    srcs_c = [
+        "common.c",
+        "foo-a.c",
+    ],
+)`},
+	})
+}
+
+func TestCcLibrarySharedStrip(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared stripping",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    strip: {
+        keep_symbols: false,
+        keep_symbols_and_debug_frame: true,
+        keep_symbols_list: ["sym", "sym2"],
+        all: true,
+        none: false,
+    },
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    strip = {
+        "all": True,
+        "keep_symbols": False,
+        "keep_symbols_and_debug_frame": True,
+        "keep_symbols_list": [
+            "sym",
+            "sym2",
+        ],
+        "none": False,
+    },
+)`},
+	})
+}
+
+func TestCcLibrarySharedVersionScript(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared version script",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem: map[string]string{
+			"version_script": "",
+		},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    version_script: "version_script",
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    version_script = "version_script",
+)`},
+	})
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 5c9afbf..91b7478 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -184,19 +184,13 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
+    absolute_includes = [
+        "include_dir_1",
+        "include_dir_2",
+    ],
     copts = [
         "-Dflag1",
         "-Dflag2",
-        "-Iinclude_dir_1",
-        "-I$(BINDIR)/include_dir_1",
-        "-Iinclude_dir_2",
-        "-I$(BINDIR)/include_dir_2",
-        "-Ilocal_include_dir_1",
-        "-I$(BINDIR)/local_include_dir_1",
-        "-Ilocal_include_dir_2",
-        "-I$(BINDIR)/local_include_dir_2",
-        "-I.",
-        "-I$(BINDIR)/.",
     ],
     export_includes = [
         "export_include_dir_1",
@@ -208,7 +202,11 @@
         ":static_lib_1",
         ":static_lib_2",
     ],
-    linkstatic = True,
+    local_includes = [
+        "local_include_dir_1",
+        "local_include_dir_2",
+        ".",
+    ],
     srcs = [
         "foo_static1.cc",
         "foo_static2.cc",
@@ -244,21 +242,15 @@
 		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
-    srcs: [
-    ],
+    srcs: [],
     include_dirs: [
-  "subpackage",
+        "subpackage",
     ],
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-Isubpackage",
-        "-I$(BINDIR)/subpackage",
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
+    absolute_includes = ["subpackage"],
+    local_includes = ["."],
 )`},
 	})
 }
@@ -284,7 +276,6 @@
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
     export_includes = ["subpackage"],
-    linkstatic = True,
 )`},
 	})
 }
@@ -310,7 +301,6 @@
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
     export_system_includes = ["subpackage"],
-    linkstatic = True,
 )`},
 	})
 }
@@ -347,20 +337,16 @@
 		blueprint: soongCcLibraryStaticPreamble,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-Isubpackage/subsubpackage",
-        "-I$(BINDIR)/subpackage/subsubpackage",
-        "-Isubpackage2",
-        "-I$(BINDIR)/subpackage2",
-        "-Isubpackage3/subsubpackage",
-        "-I$(BINDIR)/subpackage3/subsubpackage",
-        "-Isubpackage/subsubpackage2",
-        "-I$(BINDIR)/subpackage/subsubpackage2",
-        "-Isubpackage",
-        "-I$(BINDIR)/subpackage",
+    absolute_includes = [
+        "subpackage/subsubpackage",
+        "subpackage2",
+        "subpackage3/subsubpackage",
     ],
     export_includes = ["./exported_subsubpackage"],
-    linkstatic = True,
+    local_includes = [
+        "subsubpackage2",
+        ".",
+    ],
 )`},
 	})
 }
@@ -386,13 +372,8 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-Isubpackage",
-        "-I$(BINDIR)/subpackage",
-        "-Isubpackage2",
-        "-I$(BINDIR)/subpackage2",
-    ],
-    linkstatic = True,
+    absolute_includes = ["subpackage"],
+    local_includes = ["subpackage2"],
 )`},
 	})
 }
@@ -420,15 +401,11 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-Isubpackage",
-        "-I$(BINDIR)/subpackage",
-        "-Isubpackage2",
-        "-I$(BINDIR)/subpackage2",
-        "-I.",
-        "-I$(BINDIR)/.",
+    absolute_includes = ["subpackage"],
+    local_includes = [
+        "subpackage2",
+        ".",
     ],
-    linkstatic = True,
 )`},
 	})
 }
@@ -460,7 +437,6 @@
         "//build/bazel/platforms/arch:arm64": [":static_dep"],
         "//conditions:default": [],
     }),
-    linkstatic = True,
     whole_archive_deps = select({
         "//build/bazel/platforms/arch:arm64": [":static_dep2"],
         "//conditions:default": [],
@@ -496,7 +472,6 @@
         "//build/bazel/platforms/os:android": [":static_dep"],
         "//conditions:default": [],
     }),
-    linkstatic = True,
     whole_archive_deps = select({
         "//build/bazel/platforms/os:android": [":static_dep2"],
         "//conditions:default": [],
@@ -546,7 +521,6 @@
         "//build/bazel/platforms/os:android": [":static_dep3"],
         "//conditions:default": [],
     }),
-    linkstatic = True,
     whole_archive_deps = [":static_dep2"],
 )`},
 	})
@@ -572,7 +546,6 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    linkstatic = True,
     srcs_c = [
         "common.c",
         "foo-a.c",
@@ -600,7 +573,6 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["foo-arm.c"],
         "//conditions:default": [],
@@ -633,7 +605,6 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["for-arm.c"],
         "//conditions:default": ["not-for-arm.c"],
@@ -668,7 +639,6 @@
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
@@ -719,7 +689,6 @@
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
@@ -779,7 +748,6 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    linkstatic = True,
     srcs = ["common.cc"] + select({
         "//build/bazel/platforms/arch:arm": [],
         "//conditions:default": ["foo-no-arm.cc"],
@@ -813,7 +781,6 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    linkstatic = True,
     srcs = ["common.cc"] + select({
         "//build/bazel/platforms/arch:arm": [],
         "//build/bazel/platforms/arch:x86": [
@@ -846,7 +813,6 @@
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
     implementation_deps = [":static_dep"],
-    linkstatic = True,
 )`},
 	})
 }
@@ -873,7 +839,6 @@
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["for-lib32.c"],
         "//build/bazel/platforms/arch:x86": ["for-lib32.c"],
@@ -908,7 +873,6 @@
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static2",
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-lib32.c",
@@ -976,7 +940,6 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static3",
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
@@ -1091,7 +1054,6 @@
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static3",
-    linkstatic = True,
     srcs = [
         "//dep:generated_hdr_other_pkg",
         "//dep:generated_src_other_pkg",
@@ -1147,7 +1109,6 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    linkstatic = True,
     srcs_c = select({
         "//build/bazel/platforms/os:android": ["android_src.c"],
         "//conditions:default": [],
@@ -1199,7 +1160,6 @@
         "//build/bazel/product_variables:malloc_zero_contents": ["-Wmalloc_zero_contents"],
         "//conditions:default": [],
     }),
-    linkstatic = True,
     srcs_c = ["common.c"],
 )`},
 	})
@@ -1268,7 +1228,6 @@
         "//build/bazel/product_variables:malloc_not_svelte-x86": ["-Wlib32_malloc_not_svelte"],
         "//conditions:default": [],
     }),
-    linkstatic = True,
     srcs_c = ["common.c"],
 )`},
 	})
@@ -1298,7 +1257,6 @@
         "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
         "//conditions:default": [],
     }),
-    linkstatic = True,
     srcs_as = ["common.S"],
 )`},
 	})
@@ -1319,7 +1277,6 @@
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "root_empty",
-    linkstatic = True,
     system_dynamic_deps = [],
 )`},
 	})
@@ -1346,7 +1303,6 @@
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "static_empty",
-    linkstatic = True,
     system_dynamic_deps = [],
 )`},
 	})
@@ -1371,7 +1327,6 @@
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "target_bionic_empty",
-    linkstatic = True,
     system_dynamic_deps = [],
 )`},
 	})
@@ -1400,7 +1355,6 @@
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "target_linux_bionic_empty",
-    linkstatic = True,
     system_dynamic_deps = [],
 )`},
 	})
@@ -1427,7 +1381,6 @@
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "target_bionic",
-    linkstatic = True,
     system_dynamic_deps = select({
         "//build/bazel/platforms/os:bionic": [":libc"],
         "//conditions:default": [],
@@ -1459,7 +1412,6 @@
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "target_linux_bionic",
-    linkstatic = True,
     system_dynamic_deps = [":libc"] + select({
         "//build/bazel/platforms/os:linux_bionic": [":libm"],
         "//conditions:default": [],
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 4bda539..b0a88ae 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -65,10 +65,10 @@
         "-Wno-gcc-compat",
         "-Wall",
         "-Werror",
-        "-Iinclude",
-        "-I$(BINDIR)/include",
-        "-I.",
-        "-I$(BINDIR)/.",
+    ],
+    local_includes = [
+        "include",
+        ".",
     ],
     srcs = ["a/b/c.c"],
 )`,
@@ -113,9 +113,8 @@
         "-Wall",
         "-Werror",
         "-fno-addrsig",
-        "-I.",
-        "-I$(BINDIR)/.",
     ],
+    local_includes = ["."],
     srcs = ["a/b/c.c"],
 )`,
 		}})
diff --git a/bp2build/compatibility.go b/bp2build/compatibility.go
deleted file mode 100644
index 5baa524..0000000
--- a/bp2build/compatibility.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package bp2build
-
-import (
-	"android/soong/bazel"
-	"fmt"
-)
-
-// Data from the code generation process that is used to improve compatibility
-// between build systems.
-type CodegenCompatLayer struct {
-	// A map from the original module name to the generated/handcrafted Bazel
-	// label for legacy build systems to be able to build a fully-qualified
-	// Bazel target from an unique module name.
-	NameToLabelMap map[string]string
-}
-
-// Log an entry of module name -> Bazel target label.
-func (compatLayer CodegenCompatLayer) AddNameToLabelEntry(name, label string) {
-	// The module name may be prefixed with bazel.BazelTargetModuleNamePrefix if
-	// generated from bp2build.
-	name = bazel.StripNamePrefix(name)
-	if existingLabel, ok := compatLayer.NameToLabelMap[name]; ok {
-		panic(fmt.Errorf(
-			"Module '%s' maps to more than one Bazel target label: %s, %s. "+
-				"This shouldn't happen. It probably indicates a bug with the bp2build internals.",
-			name,
-			existingLabel,
-			label))
-	}
-	compatLayer.NameToLabelMap[name] = label
-}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 75bc2b4..354abf6 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -16,29 +16,19 @@
 	Contents string
 }
 
-func CreateSoongInjectionFiles(compatLayer CodegenCompatLayer) []BazelFile {
+func CreateSoongInjectionFiles(metrics CodegenMetrics) []BazelFile {
 	var files []BazelFile
 
 	files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
 	files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars()))
 
-	files = append(files, newFile("module_name_to_label", GeneratedBuildFileName, nameToLabelAliases(compatLayer.NameToLabelMap)))
+	files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n")))
 
 	return files
 }
 
-func nameToLabelAliases(nameToLabelMap map[string]string) string {
-	ret := make([]string, len(nameToLabelMap))
-
-	for k, v := range nameToLabelMap {
-		// v is the fully qualified label rooted at '//'
-		ret = append(ret, fmt.Sprintf(
-			`alias(
-    name = "%s",
-    actual = "@%s",
-)`, k, v))
-	}
-	return strings.Join(ret, "\n\n")
+func convertedModules(convertedModules []string) string {
+	return strings.Join(convertedModules, "\n")
 }
 
 func CreateBazelFiles(
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 56ea589..dfa1a9e 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -80,7 +80,7 @@
 }
 
 func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
-	files := CreateSoongInjectionFiles(CodegenCompatLayer{})
+	files := CreateSoongInjectionFiles(CodegenMetrics{})
 
 	expectedFilePaths := []bazelFilepath{
 		{
@@ -92,8 +92,8 @@
 			basename: "constants.bzl",
 		},
 		{
-			dir:      "module_name_to_label",
-			basename: GeneratedBuildFileName,
+			dir:      "metrics",
+			basename: "converted_modules.txt",
 		},
 	}
 
@@ -107,9 +107,5 @@
 		if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename {
 			t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename)
 		}
-
-		if expectedFile.basename != GeneratedBuildFileName && actualFile.Contents == "" {
-			t.Errorf("Contents of %s unexpected empty.", actualFile)
-		}
 	}
 }
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
index a991180..f3bc1ba 100644
--- a/bp2build/genrule_conversion_test.go
+++ b/bp2build/genrule_conversion_test.go
@@ -267,7 +267,8 @@
 		}
 
 		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
+		android.FailIfErrored(t, err)
 		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
 			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
 		} else {
@@ -461,7 +462,8 @@
 		android.FailIfErrored(t, errs)
 
 		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+		android.FailIfErrored(t, err)
 		if actualCount := len(bazelTargets); actualCount != 1 {
 			t.Fatalf("%s: Expected 1 bazel target, got %d", testCase.description, actualCount)
 		}
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 65b06c6..55b928b 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -3,6 +3,7 @@
 import (
 	"android/soong/android"
 	"fmt"
+	"strings"
 )
 
 // Simple metrics struct to collect information about a Blueprint to BUILD
@@ -16,6 +17,10 @@
 
 	// Total number of handcrafted targets
 	handCraftedTargetCount int
+
+	moduleWithUnconvertedDepsMsgs []string
+
+	convertedModules []string
 }
 
 // Print the codegen metrics to stdout.
@@ -27,8 +32,16 @@
 		generatedTargetCount += count
 	}
 	fmt.Printf(
-		"[bp2build] Generated %d total BUILD targets and included %d handcrafted BUILD targets from %d Android.bp modules.\n",
+		"[bp2build] Generated %d total BUILD targets and included %d handcrafted BUILD targets from %d Android.bp modules.\n With %d modules with unconverted deps \n\t%s",
 		generatedTargetCount,
 		metrics.handCraftedTargetCount,
-		metrics.TotalModuleCount)
+		metrics.TotalModuleCount,
+		len(metrics.moduleWithUnconvertedDepsMsgs),
+		strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"))
+}
+
+func (metrics CodegenMetrics) AddConvertedModule(moduleName string) {
+	// Undo prebuilt_ module name prefix modifications
+	moduleName = android.RemoveOptionalPrebuiltPrefix(moduleName)
+	metrics.convertedModules = append(metrics.convertedModules, moduleName)
 }
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 3ebe63d..74084b1 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -39,9 +39,8 @@
 func checkError(t *testing.T, errs []error, expectedErr error) bool {
 	t.Helper()
 
-	// expectedErr is not nil, find it in the list of errors
 	if len(errs) != 1 {
-		t.Errorf("Expected only 1 error, got %d: %q", len(errs), errs)
+		return false
 	}
 	if errs[0].Error() == expectedErr.Error() {
 		return true
@@ -83,6 +82,7 @@
 	filesystem                         map[string]string
 	dir                                string
 	expectedErr                        error
+	unconvertedDepsMode                unconvertedDepsMode
 }
 
 func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) {
@@ -126,7 +126,13 @@
 		checkDir = tc.dir
 	}
 	codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-	bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+	codegenCtx.unconvertedDepMode = tc.unconvertedDepsMode
+	bazelTargets, errs := generateBazelTargetsForDir(codegenCtx, checkDir)
+	if tc.expectedErr != nil && checkError(t, errs, tc.expectedErr) {
+		return
+	} else {
+		android.FailIfErrored(t, errs)
+	}
 	if actualCount, expectedCount := len(bazelTargets), len(tc.expectedBazelTargets); actualCount != expectedCount {
 		t.Errorf("%s: Expected %d bazel target, got %d; %v",
 			tc.description, expectedCount, actualCount, bazelTargets)
@@ -148,7 +154,18 @@
 	Nested_prop string
 }
 
+type EmbeddedProps struct {
+	Embedded_prop string
+}
+
+type OtherEmbeddedProps struct {
+	Other_embedded_prop string
+}
+
 type customProps struct {
+	EmbeddedProps
+	*OtherEmbeddedProps
+
 	Bool_prop     bool
 	Bool_ptr_prop *bool
 	// Ensure that properties tagged `blueprint:mutated` are omitted
@@ -246,7 +263,17 @@
 	return m
 }
 
+type EmbeddedAttr struct {
+	Embedded_attr string
+}
+
+type OtherEmbeddedAttr struct {
+	Other_embedded_attr string
+}
+
 type customBazelModuleAttributes struct {
+	EmbeddedAttr
+	*OtherEmbeddedAttr
 	String_prop      string
 	String_list_prop []string
 	Arch_paths       bazel.LabelListAttribute
@@ -275,6 +302,10 @@
 			String_list_prop: m.props.String_list_prop,
 			Arch_paths:       paths,
 		}
+		attrs.Embedded_attr = m.props.Embedded_prop
+		if m.props.OtherEmbeddedProps != nil {
+			attrs.OtherEmbeddedAttr = &OtherEmbeddedAttr{Other_embedded_attr: m.props.OtherEmbeddedProps.Other_embedded_prop}
+		}
 
 		props := bazel.BazelTargetModuleProperties{
 			Rule_class: "custom",
@@ -316,10 +347,10 @@
 }
 
 // Helper method for tests to easily access the targets in a dir.
-func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) BazelTargets {
+func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) (BazelTargets, []error) {
 	// TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely
-	buildFileToTargets, _, _ := GenerateBazelTargets(codegenCtx, false)
-	return buildFileToTargets[dir]
+	res, err := GenerateBazelTargets(codegenCtx, false)
+	return res.buildFileToTargets[dir], err
 }
 
 func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
diff --git a/cc/bp2build.go b/cc/bp2build.go
index fffb093..67ea70e 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -111,24 +111,23 @@
 	return
 }
 
-// bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
-func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
+// bp2BuildParseLibProps returns the attributes for a variant of a cc_library.
+func bp2BuildParseLibProps(ctx android.TopDownMutatorContext, module *Module, isStatic bool) staticOrSharedAttributes {
 	lib, ok := module.compiler.(*libraryDecorator)
 	if !ok {
 		return staticOrSharedAttributes{}
 	}
+	return bp2buildParseStaticOrSharedProps(ctx, module, lib, isStatic)
+}
 
-	return bp2buildParseStaticOrSharedProps(ctx, module, lib, false)
+// bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
+func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
+	return bp2BuildParseLibProps(ctx, module, false)
 }
 
 // bp2buildParseStaticProps returns the attributes for the static variant of a cc_library.
 func bp2BuildParseStaticProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
-	lib, ok := module.compiler.(*libraryDecorator)
-	if !ok {
-		return staticOrSharedAttributes{}
-	}
-
-	return bp2buildParseStaticOrSharedProps(ctx, module, lib, true)
+	return bp2BuildParseLibProps(ctx, module, true)
 }
 
 func bp2buildParseStaticOrSharedProps(ctx android.TopDownMutatorContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
@@ -178,6 +177,7 @@
 	Src bazel.LabelAttribute
 }
 
+// NOTE: Used outside of Soong repo project, in the clangprebuilts.go bootstrap_go_package
 func Bp2BuildParsePrebuiltLibraryProps(ctx android.TopDownMutatorContext, module *Module) prebuiltAttributes {
 	var srcLabelAttribute bazel.LabelAttribute
 
@@ -216,6 +216,9 @@
 	srcs     bazel.LabelListAttribute
 
 	rtti bazel.BoolAttribute
+
+	localIncludes    bazel.StringListAttribute
+	absoluteIncludes bazel.StringListAttribute
 }
 
 // bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
@@ -226,28 +229,8 @@
 	var conlyFlags bazel.StringListAttribute
 	var cppFlags bazel.StringListAttribute
 	var rtti bazel.BoolAttribute
-
-	// Creates the -I flags for a directory, while making the directory relative
-	// to the exec root for Bazel to work.
-	includeFlags := func(dir string) []string {
-		// filepath.Join canonicalizes the path, i.e. it takes care of . or .. elements.
-		moduleDirRootedPath := filepath.Join(ctx.ModuleDir(), dir)
-		return []string{
-			"-I" + moduleDirRootedPath,
-			// Include the bindir-rooted path (using make variable substitution). This most
-			// closely matches Bazel's native include path handling, which allows for dependency
-			// on generated headers in these directories.
-			// TODO(b/188084383): Handle local include directories in Bazel.
-			"-I$(BINDIR)/" + moduleDirRootedPath,
-		}
-	}
-
-	// Parse the list of module-relative include directories (-I).
-	parseLocalIncludeDirs := func(baseCompilerProps *BaseCompilerProperties) []string {
-		// include_dirs are root-relative, not module-relative.
-		includeDirs := bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Include_dirs)
-		return append(includeDirs, baseCompilerProps.Local_include_dirs...)
-	}
+	var localIncludes bazel.StringListAttribute
+	var absoluteIncludes bazel.StringListAttribute
 
 	parseCommandLineFlags := func(soongFlags []string) []string {
 		var result []string
@@ -285,18 +268,14 @@
 
 				archVariantCopts := parseCommandLineFlags(baseCompilerProps.Cflags)
 				archVariantAsflags := parseCommandLineFlags(baseCompilerProps.Asflags)
-				for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
-					archVariantCopts = append(archVariantCopts, includeFlags(dir)...)
-					archVariantAsflags = append(archVariantAsflags, includeFlags(dir)...)
+
+				localIncludeDirs := baseCompilerProps.Local_include_dirs
+				if axis == bazel.NoConfigAxis && includeBuildDirectory(baseCompilerProps.Include_build_directory) {
+					localIncludeDirs = append(localIncludeDirs, ".")
 				}
 
-				if axis == bazel.NoConfigAxis {
-					if includeBuildDirectory(baseCompilerProps.Include_build_directory) {
-						flags := includeFlags(".")
-						archVariantCopts = append(archVariantCopts, flags...)
-						archVariantAsflags = append(archVariantAsflags, flags...)
-					}
-				}
+				absoluteIncludes.SetSelectValue(axis, config, baseCompilerProps.Include_dirs)
+				localIncludes.SetSelectValue(axis, config, localIncludeDirs)
 
 				copts.SetSelectValue(axis, config, archVariantCopts)
 				asFlags.SetSelectValue(axis, config, archVariantAsflags)
@@ -308,6 +287,8 @@
 	}
 
 	srcs.ResolveExcludes()
+	absoluteIncludes.DeduplicateAxesFromBase()
+	localIncludes.DeduplicateAxesFromBase()
 
 	productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
 		"Cflags":   &copts,
@@ -331,14 +312,16 @@
 	srcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, srcs)
 
 	return compilerAttributes{
-		copts:      copts,
-		srcs:       srcs,
-		asFlags:    asFlags,
-		asSrcs:     asSrcs,
-		cSrcs:      cSrcs,
-		conlyFlags: conlyFlags,
-		cppFlags:   cppFlags,
-		rtti:       rtti,
+		copts:            copts,
+		srcs:             srcs,
+		asFlags:          asFlags,
+		asSrcs:           asSrcs,
+		cSrcs:            cSrcs,
+		conlyFlags:       conlyFlags,
+		cppFlags:         cppFlags,
+		rtti:             rtti,
+		localIncludes:    localIncludes,
+		absoluteIncludes: absoluteIncludes,
 	}
 }
 
diff --git a/cc/builder.go b/cc/builder.go
index 6a4d940..4b0a4b6 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -21,6 +21,7 @@
 import (
 	"path/filepath"
 	"runtime"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -198,8 +199,18 @@
 	// Rule for invoking clang-tidy (a clang-based linter).
 	clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && $tidyVars $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && touch $out",
-			CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
+			Depfile: "${out}.d",
+			Deps:    blueprint.DepsGCC,
+			// Pick bash because some machines with old /bin/sh cannot handle arrays.
+			// All $cFlags and $tidyFlags should have single quotes escaped.
+			// Assume no single quotes in other parameters like $in, $out, $ccCmd.
+			Command: "/bin/bash -c 'SRCF=$in; TIDYF=$out; CLANGFLAGS=($cFlags); " +
+				"rm -f $$TIDYF $${TIDYF}.d && " +
+				"${config.CcWrapper}$ccCmd \"$${CLANGFLAGS[@]}\" -E -o /dev/null $$SRCF " +
+				"-MQ $$TIDYF -MD -MF $${TIDYF}.d && " +
+				"$tidyVars $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $$SRCF " +
+				"-- \"$${CLANGFLAGS[@]}\" && touch $$TIDYF'",
+			CommandDeps: []string{"${config.ClangBin}/clang-tidy", "$ccCmd"},
 		},
 		&remoteexec.REParams{
 			Labels:               map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
@@ -213,7 +224,7 @@
 			// (1) New timestamps trigger clang and clang-tidy compilations again.
 			// (2) Changing source files caused concurrent clang or clang-tidy jobs to crash.
 			Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
-		}, []string{"cFlags", "tidyFlags", "tidyVars"}, []string{})
+		}, []string{"ccCmd", "cFlags", "tidyFlags", "tidyVars"}, []string{})
 
 	_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
 
@@ -436,6 +447,12 @@
 	}
 }
 
+func escapeSingleQuotes(s string) string {
+	// Replace single quotes to work when embedded in a single quoted string for bash.
+	// Relying on string concatenation of bash to get A'B from quoted 'A'\''B'.
+	return strings.Replace(s, `'`, `'\''`, -1)
+}
+
 // Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
 func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles android.Paths,
 	flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
@@ -510,6 +527,32 @@
 	cppflags += " ${config.NoOverrideGlobalCflags}"
 	toolingCppflags += " ${config.NoOverrideGlobalCflags}"
 
+	// Multiple source files have build rules usually share the same cFlags or tidyFlags.
+	// Define only one version in this module and share it in multiple build rules.
+	// To simplify the code, the shared variables are all named as $flags<nnn>.
+	numSharedFlags := 0
+	flagsMap := make(map[string]string)
+
+	// Share flags only when there are multiple files or tidy rules.
+	var hasMultipleRules = len(srcFiles) > 1 || flags.tidy
+
+	var shareFlags = func(kind string, flags string) string {
+		if !hasMultipleRules || len(flags) < 60 {
+			// Modules have long names and so do the module variables.
+			// It does not save space by replacing a short name with a long one.
+			return flags
+		}
+		mapKey := kind + flags
+		n, ok := flagsMap[mapKey]
+		if !ok {
+			numSharedFlags += 1
+			n = strconv.Itoa(numSharedFlags)
+			flagsMap[mapKey] = n
+			ctx.Variable(pctx, kind+n, flags)
+		}
+		return "$" + kind + n
+	}
+
 	for i, srcFile := range srcFiles {
 		objFile := android.ObjPathWithExt(ctx, subdir, srcFile, "o")
 
@@ -526,7 +569,7 @@
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"asFlags": flags.globalYasmFlags + " " + flags.localYasmFlags,
+					"asFlags": shareFlags("asFlags", flags.globalYasmFlags+" "+flags.localYasmFlags),
 				},
 			})
 			continue
@@ -540,7 +583,7 @@
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
 					"windresCmd": mingwCmd(flags.toolchain, "windres"),
-					"flags":      flags.toolchain.WindresFlags(),
+					"flags":      shareFlags("flags", flags.toolchain.WindresFlags()),
 				},
 			})
 			continue
@@ -608,8 +651,8 @@
 			Implicits:       cFlagsDeps,
 			OrderOnly:       pathDeps,
 			Args: map[string]string{
-				"cFlags": moduleFlags,
-				"ccCmd":  ccCmd,
+				"cFlags": shareFlags("cFlags", moduleFlags),
+				"ccCmd":  ccCmd, // short and not shared
 			},
 		})
 
@@ -624,7 +667,7 @@
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"cFlags": moduleFlags,
+					"cFlags": shareFlags("cFlags", moduleFlags),
 				},
 			})
 			kytheFiles = append(kytheFiles, kytheFile)
@@ -645,15 +688,13 @@
 				Description: "clang-tidy " + srcFile.Rel(),
 				Output:      tidyFile,
 				Input:       srcFile,
-				// We must depend on objFile, since clang-tidy doesn't
-				// support exporting dependencies.
-				Implicit:  objFile,
-				Implicits: cFlagsDeps,
-				OrderOnly: pathDeps,
+				Implicits:   cFlagsDeps,
+				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"cFlags":    moduleToolingFlags,
-					"tidyFlags": config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags),
-					"tidyVars":  tidyVars,
+					"ccCmd":     ccCmd,
+					"cFlags":    shareFlags("cFlags", escapeSingleQuotes(moduleToolingFlags)),
+					"tidyFlags": shareFlags("tidyFlags", escapeSingleQuotes(config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags))),
+					"tidyVars":  tidyVars, // short and not shared
 				},
 			})
 		}
@@ -675,8 +716,8 @@
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"cFlags":     moduleToolingFlags,
-					"exportDirs": flags.sAbiFlags,
+					"cFlags":     shareFlags("cFlags", moduleToolingFlags),
+					"exportDirs": shareFlags("exportDirs", flags.sAbiFlags),
 				},
 			})
 		}
diff --git a/cc/cc.go b/cc/cc.go
index b0c0299..d2f8a12 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -31,6 +31,7 @@
 	"android/soong/cc/config"
 	"android/soong/fuzz"
 	"android/soong/genrule"
+	"android/soong/snapshot"
 )
 
 func init() {
@@ -1813,15 +1814,6 @@
 
 	flags.AssemblerWithCpp = inList("-xassembler-with-cpp", flags.Local.AsFlags)
 
-	// Optimization to reduce size of build.ninja
-	// Replace the long list of flags for each file with a module-local variable
-	ctx.Variable(pctx, "cflags", strings.Join(flags.Local.CFlags, " "))
-	ctx.Variable(pctx, "cppflags", strings.Join(flags.Local.CppFlags, " "))
-	ctx.Variable(pctx, "asflags", strings.Join(flags.Local.AsFlags, " "))
-	flags.Local.CFlags = []string{"$cflags"}
-	flags.Local.CppFlags = []string{"$cppflags"}
-	flags.Local.AsFlags = []string{"$asflags"}
-
 	var objs Objects
 	if c.compiler != nil {
 		objs = c.compiler.compile(ctx, flags, deps)
@@ -3401,6 +3393,8 @@
 	return c.IsStubs() || c.Target().NativeBridge == android.NativeBridgeEnabled
 }
 
+var _ snapshot.RelativeInstallPath = (*Module)(nil)
+
 //
 // Defaults
 //
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 53a7306..3caa688 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -31,23 +31,15 @@
 	"-fno-tree-sra",
 	"-fprefetch-loop-arrays",
 	"-funswitch-loops",
-	"-Werror=unused-but-set-parameter",
-	"-Werror=unused-but-set-variable",
 	"-Wmaybe-uninitialized",
 	"-Wno-error=clobbered",
 	"-Wno-error=maybe-uninitialized",
-	"-Wno-error=unused-but-set-parameter",
-	"-Wno-error=unused-but-set-variable",
 	"-Wno-extended-offsetof",
 	"-Wno-free-nonheap-object",
 	"-Wno-literal-suffix",
 	"-Wno-maybe-uninitialized",
 	"-Wno-old-style-declaration",
-	"-Wno-unused-but-set-parameter",
-	"-Wno-unused-but-set-variable",
 	"-Wno-unused-local-typedefs",
-	"-Wunused-but-set-parameter",
-	"-Wunused-but-set-variable",
 	"-fdiagnostics-color",
 	// http://b/153759688
 	"-fuse-init-array",
diff --git a/cc/library.go b/cc/library.go
index 28bd741..703d57f 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -205,6 +205,7 @@
 	RegisterLibraryBuildComponents(android.InitRegistrationContext)
 
 	android.RegisterBp2BuildMutator("cc_library_static", CcLibraryStaticBp2Build)
+	android.RegisterBp2BuildMutator("cc_library_shared", CcLibrarySharedBp2Build)
 	android.RegisterBp2BuildMutator("cc_library", CcLibraryBp2Build)
 }
 
@@ -216,6 +217,7 @@
 	ctx.RegisterModuleType("cc_library_host_shared", LibraryHostSharedFactory)
 }
 
+// TODO(b/199902614): Can this be factored to share with the other Attributes?
 // For bp2build conversion.
 type bazelCcLibraryAttributes struct {
 	// Attributes pertaining to both static and shared variants.
@@ -236,6 +238,8 @@
 	System_dynamic_deps    bazel.LabelListAttribute
 	Export_includes        bazel.StringListAttribute
 	Export_system_includes bazel.StringListAttribute
+	Local_includes         bazel.StringListAttribute
+	Absolute_includes      bazel.StringListAttribute
 	Linkopts               bazel.StringListAttribute
 	Use_libcrt             bazel.BoolAttribute
 	Rtti                   bazel.BoolAttribute
@@ -272,7 +276,7 @@
 	// converted, but not their shared variants. For these modules, delegate to
 	// the cc_library_static bp2build converter temporarily instead.
 	if android.GenerateCcLibraryStaticOnly(ctx) {
-		ccLibraryStaticBp2BuildInternal(ctx, m)
+		ccSharedOrStaticBp2BuildMutatorInternal(ctx, m, "cc_library_static")
 		return
 	}
 
@@ -307,6 +311,8 @@
 		System_dynamic_deps:    linkerAttrs.systemDynamicDeps,
 		Export_includes:        exportedIncludes.Includes,
 		Export_system_includes: exportedIncludes.SystemIncludes,
+		Local_includes:         compilerAttrs.localIncludes,
+		Absolute_includes:      compilerAttrs.absoluteIncludes,
 		Linkopts:               linkerAttrs.linkopts,
 		Use_libcrt:             linkerAttrs.useLibcrt,
 		Rtti:                   compilerAttrs.rtti,
@@ -364,6 +370,7 @@
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyShared()
 	module.sdkMemberTypes = []android.SdkMemberType{sharedLibrarySdkMemberType}
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -597,7 +604,10 @@
 
 	handler.module.linker.(*libraryDecorator).unstrippedOutputFile = outputFilePath
 
-	tocFile := getTocFile(ctx, label, ccInfo.OutputFiles)
+	var tocFile android.OptionalPath
+	if len(ccInfo.TocFile) > 0 {
+		tocFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, ccInfo.TocFile))
+	}
 	handler.module.linker.(*libraryDecorator).tocFile = tocFile
 
 	ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
@@ -610,25 +620,6 @@
 	return true
 }
 
-// getTocFile looks for the .so.toc file in the target's output files, if any. The .so.toc file
-// contains the table of contents of all symbols of a shared object.
-func getTocFile(ctx android.ModuleContext, label string, outputFiles []string) android.OptionalPath {
-	var tocFile string
-	for _, file := range outputFiles {
-		if strings.HasSuffix(file, ".so.toc") {
-			if tocFile != "" {
-				ctx.ModuleErrorf("The %s target cannot produce more than 1 .toc file.", label)
-			}
-			tocFile = file
-			// Don't break to validate that there are no multiple .toc files per .so.
-		}
-	}
-	if tocFile == "" {
-		return android.OptionalPath{}
-	}
-	return android.OptionalPathForPath(android.PathForBazelOut(ctx, tocFile))
-}
-
 func (handler *ccLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
 	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
@@ -2319,6 +2310,123 @@
 	return outputFile
 }
 
+func ccSharedOrStaticBp2BuildMutator(ctx android.TopDownMutatorContext, modType string) {
+	module, ok := ctx.Module().(*Module)
+	if !ok {
+		// Not a cc module
+		return
+	}
+	if !module.ConvertWithBp2build(ctx) {
+		return
+	}
+	if ctx.ModuleType() != modType {
+		return
+	}
+
+	ccSharedOrStaticBp2BuildMutatorInternal(ctx, module, modType)
+}
+
+func ccSharedOrStaticBp2BuildMutatorInternal(ctx android.TopDownMutatorContext, module *Module, modType string) {
+	if modType != "cc_library_static" && modType != "cc_library_shared" {
+		panic("ccSharedOrStaticBp2BuildMutatorInternal only supports cc_library_{static,shared}")
+	}
+	isStatic := modType == "cc_library_static"
+
+	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
+	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
+	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
+
+	// Append shared/static{} stanza properties. These won't be specified on
+	// cc_library_* itself, but may be specified in cc_defaults that this module
+	// depends on.
+	libSharedOrStaticAttrs := bp2BuildParseLibProps(ctx, module, isStatic)
+
+	compilerAttrs.srcs.Append(libSharedOrStaticAttrs.Srcs)
+	compilerAttrs.cSrcs.Append(libSharedOrStaticAttrs.Srcs_c)
+	compilerAttrs.asSrcs.Append(libSharedOrStaticAttrs.Srcs_as)
+	compilerAttrs.copts.Append(libSharedOrStaticAttrs.Copts)
+	linkerAttrs.exportedDeps.Append(libSharedOrStaticAttrs.Static_deps)
+	linkerAttrs.dynamicDeps.Append(libSharedOrStaticAttrs.Dynamic_deps)
+	linkerAttrs.wholeArchiveDeps.Append(libSharedOrStaticAttrs.Whole_archive_deps)
+	linkerAttrs.systemDynamicDeps.Append(libSharedOrStaticAttrs.System_dynamic_deps)
+
+	asFlags := compilerAttrs.asFlags
+	if compilerAttrs.asSrcs.IsEmpty() {
+		// Skip asflags for BUILD file simplicity if there are no assembly sources.
+		asFlags = bazel.MakeStringListAttribute(nil)
+	}
+
+	var attrs interface{}
+	if isStatic {
+		attrs = &bazelCcLibraryStaticAttributes{
+			Copts:               compilerAttrs.copts,
+			Srcs:                compilerAttrs.srcs,
+			Implementation_deps: linkerAttrs.deps,
+			Deps:                linkerAttrs.exportedDeps,
+			Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
+			Dynamic_deps:        linkerAttrs.dynamicDeps,
+			System_dynamic_deps: linkerAttrs.systemDynamicDeps,
+
+			Linkopts:               linkerAttrs.linkopts,
+			Use_libcrt:             linkerAttrs.useLibcrt,
+			Rtti:                   compilerAttrs.rtti,
+			Export_includes:        exportedIncludes.Includes,
+			Export_system_includes: exportedIncludes.SystemIncludes,
+			Local_includes:         compilerAttrs.localIncludes,
+			Absolute_includes:      compilerAttrs.absoluteIncludes,
+
+			Cppflags:   compilerAttrs.cppFlags,
+			Srcs_c:     compilerAttrs.cSrcs,
+			Conlyflags: compilerAttrs.conlyFlags,
+			Srcs_as:    compilerAttrs.asSrcs,
+			Asflags:    asFlags,
+		}
+	} else {
+		attrs = &bazelCcLibrarySharedAttributes{
+			Srcs:    compilerAttrs.srcs,
+			Srcs_c:  compilerAttrs.cSrcs,
+			Srcs_as: compilerAttrs.asSrcs,
+
+			Implementation_deps: linkerAttrs.deps,
+			Deps:                linkerAttrs.exportedDeps,
+			Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
+			Dynamic_deps:        linkerAttrs.dynamicDeps,
+			System_dynamic_deps: linkerAttrs.systemDynamicDeps,
+
+			Copts:      compilerAttrs.copts,
+			Cppflags:   compilerAttrs.cppFlags,
+			Conlyflags: compilerAttrs.conlyFlags,
+			Asflags:    asFlags,
+			Linkopts:   linkerAttrs.linkopts,
+
+			Use_libcrt: linkerAttrs.useLibcrt,
+			Rtti:       compilerAttrs.rtti,
+
+			Export_includes:        exportedIncludes.Includes,
+			Export_system_includes: exportedIncludes.SystemIncludes,
+			Local_includes:         compilerAttrs.localIncludes,
+			Absolute_includes:      compilerAttrs.absoluteIncludes,
+			Version_script:         linkerAttrs.versionScript,
+
+			Strip: stripAttributes{
+				Keep_symbols:                 linkerAttrs.stripKeepSymbols,
+				Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame,
+				Keep_symbols_list:            linkerAttrs.stripKeepSymbolsList,
+				All:                          linkerAttrs.stripAll,
+				None:                         linkerAttrs.stripNone,
+			},
+		}
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        modType,
+		Bzl_load_location: fmt.Sprintf("//build/bazel/rules:%s.bzl", modType),
+	}
+
+	ctx.CreateBazelTargetModule(module.Name(), props, attrs)
+}
+
+// TODO(b/199902614): Can this be factored to share with the other Attributes?
 type bazelCcLibraryStaticAttributes struct {
 	Copts                  bazel.StringListAttribute
 	Srcs                   bazel.LabelListAttribute
@@ -2328,11 +2436,12 @@
 	Dynamic_deps           bazel.LabelListAttribute
 	System_dynamic_deps    bazel.LabelListAttribute
 	Linkopts               bazel.StringListAttribute
-	Linkstatic             bool
 	Use_libcrt             bazel.BoolAttribute
 	Rtti                   bazel.BoolAttribute
 	Export_includes        bazel.StringListAttribute
 	Export_system_includes bazel.StringListAttribute
+	Local_includes         bazel.StringListAttribute
+	Absolute_includes      bazel.StringListAttribute
 	Hdrs                   bazel.LabelListAttribute
 
 	Cppflags   bazel.StringListAttribute
@@ -2340,78 +2449,42 @@
 	Conlyflags bazel.StringListAttribute
 	Srcs_as    bazel.LabelListAttribute
 	Asflags    bazel.StringListAttribute
-
-	Static staticOrSharedAttributes
-}
-
-func ccLibraryStaticBp2BuildInternal(ctx android.TopDownMutatorContext, module *Module) {
-	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
-	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)
-	}
-
-	// Append static{} stanza properties. These won't be specified on
-	// cc_library_static itself, but may be specified in cc_defaults that this module
-	// depends on.
-	staticAttrs := bp2BuildParseStaticProps(ctx, module)
-
-	compilerAttrs.srcs.Append(staticAttrs.Srcs)
-	compilerAttrs.cSrcs.Append(staticAttrs.Srcs_c)
-	compilerAttrs.asSrcs.Append(staticAttrs.Srcs_as)
-	compilerAttrs.copts.Append(staticAttrs.Copts)
-	linkerAttrs.exportedDeps.Append(staticAttrs.Static_deps)
-	linkerAttrs.dynamicDeps.Append(staticAttrs.Dynamic_deps)
-	linkerAttrs.wholeArchiveDeps.Append(staticAttrs.Whole_archive_deps)
-	linkerAttrs.systemDynamicDeps.Append(staticAttrs.System_dynamic_deps)
-
-	attrs := &bazelCcLibraryStaticAttributes{
-		Copts:               compilerAttrs.copts,
-		Srcs:                compilerAttrs.srcs,
-		Implementation_deps: linkerAttrs.deps,
-		Deps:                linkerAttrs.exportedDeps,
-		Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
-		Dynamic_deps:        linkerAttrs.dynamicDeps,
-		System_dynamic_deps: linkerAttrs.systemDynamicDeps,
-
-		Linkopts:               linkerAttrs.linkopts,
-		Linkstatic:             true,
-		Use_libcrt:             linkerAttrs.useLibcrt,
-		Rtti:                   compilerAttrs.rtti,
-		Export_includes:        exportedIncludes.Includes,
-		Export_system_includes: exportedIncludes.SystemIncludes,
-
-		Cppflags:   compilerAttrs.cppFlags,
-		Srcs_c:     compilerAttrs.cSrcs,
-		Conlyflags: compilerAttrs.conlyFlags,
-		Srcs_as:    compilerAttrs.asSrcs,
-		Asflags:    asFlags,
-	}
-
-	props := bazel.BazelTargetModuleProperties{
-		Rule_class:        "cc_library_static",
-		Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
-	}
-
-	ctx.CreateBazelTargetModule(module.Name(), props, attrs)
 }
 
 func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
-	module, ok := ctx.Module().(*Module)
-	if !ok {
-		// Not a cc module
-		return
-	}
-	if !module.ConvertWithBp2build(ctx) {
-		return
-	}
-	if ctx.ModuleType() != "cc_library_static" {
-		return
-	}
+	ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_static")
+}
 
-	ccLibraryStaticBp2BuildInternal(ctx, module)
+// TODO(b/199902614): Can this be factored to share with the other Attributes?
+type bazelCcLibrarySharedAttributes struct {
+	Srcs    bazel.LabelListAttribute
+	Srcs_c  bazel.LabelListAttribute
+	Srcs_as bazel.LabelListAttribute
+
+	Implementation_deps bazel.LabelListAttribute
+	Deps                bazel.LabelListAttribute
+	Whole_archive_deps  bazel.LabelListAttribute
+	Dynamic_deps        bazel.LabelListAttribute
+	System_dynamic_deps bazel.LabelListAttribute
+
+	Linkopts   bazel.StringListAttribute
+	Use_libcrt bazel.BoolAttribute
+	Rtti       bazel.BoolAttribute
+	Strip      stripAttributes
+
+	Export_includes        bazel.StringListAttribute
+	Export_system_includes bazel.StringListAttribute
+	Local_includes         bazel.StringListAttribute
+	Absolute_includes      bazel.StringListAttribute
+	Hdrs                   bazel.LabelListAttribute
+	Version_script         bazel.LabelAttribute
+
+	Copts      bazel.StringListAttribute
+	Cppflags   bazel.StringListAttribute
+	Conlyflags bazel.StringListAttribute
+	Asflags    bazel.StringListAttribute
+}
+
+func CcLibrarySharedBp2Build(ctx android.TopDownMutatorContext) {
+	ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_shared")
 }
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 30a81cc..14b90c1 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -103,7 +103,6 @@
 }
 
 type bazelCcLibraryHeadersAttributes struct {
-	Copts                  bazel.StringListAttribute
 	Hdrs                   bazel.LabelListAttribute
 	Export_includes        bazel.StringListAttribute
 	Export_system_includes bazel.StringListAttribute
@@ -128,11 +127,9 @@
 	}
 
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
-	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
-		Copts:                  compilerAttrs.copts,
 		Export_includes:        exportedIncludes.Includes,
 		Export_system_includes: exportedIncludes.SystemIncludes,
 		Implementation_deps:    linkerAttrs.deps,
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 1866ff3..559e940 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -265,8 +265,8 @@
 	// values where necessary.
 	for _, propertyInfo := range includeDirProperties {
 		// Calculate the base directory in the snapshot into which the files will be copied.
-		// lib.archType is "" for common properties.
-		targetDir := filepath.Join(libInfo.OsPrefix(), libInfo.archType, propertyInfo.snapshotDir)
+		// lib.archSubDir is "" for common properties.
+		targetDir := filepath.Join(libInfo.OsPrefix(), libInfo.archSubDir, propertyInfo.snapshotDir)
 
 		propertyName := propertyInfo.propertyName
 
@@ -334,7 +334,7 @@
 
 // path to the native library. Relative to <sdk_root>/<api_dir>
 func nativeLibraryPathFor(lib *nativeLibInfoProperties) string {
-	return filepath.Join(lib.OsPrefix(), lib.archType,
+	return filepath.Join(lib.OsPrefix(), lib.archSubDir,
 		nativeStubDir, lib.outputFile.Base())
 }
 
@@ -347,9 +347,12 @@
 
 	memberType *librarySdkMemberType
 
-	// archType is not exported as if set (to a non default value) it is always arch specific.
-	// This is "" for common properties.
-	archType string
+	// archSubDir is the subdirectory within the OS directory in the sdk snapshot into which arch
+	// specific files will be copied.
+	//
+	// It is not exported since any value other than "" is always going to be arch specific.
+	// This is "" for non-arch specific common properties.
+	archSubDir string
 
 	// The list of possibly common exported include dirs.
 	//
@@ -433,7 +436,7 @@
 	exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate(
 		exportedInfo.IncludeDirs, isGeneratedHeaderDirectory)
 
-	p.archType = ccModule.Target().Arch.ArchType.String()
+	p.archSubDir = ccModule.Target().Arch.ArchType.String()
 
 	// Make sure that the include directories are unique.
 	p.ExportedIncludeDirs = android.FirstUniquePaths(exportedIncludeDirs)
diff --git a/cc/library_test.go b/cc/library_test.go
index 6b349b6..7ddfaa7 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -320,3 +320,48 @@
 		libfoo.Args["ldFlags"], "-Wl,--dynamic-list,foo.dynamic.txt")
 
 }
+
+func TestCcLibrarySharedWithBazel(t *testing.T) {
+	bp := `
+cc_library_shared {
+	name: "foo",
+	srcs: ["foo.cc"],
+	bazel_module: { label: "//foo/bar:bar" },
+}`
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.BazelContext = android.MockBazelContext{
+		OutputBaseDir: "outputbase",
+		LabelToCcInfo: map[string]cquery.CcInfo{
+			"//foo/bar:bar": cquery.CcInfo{
+				CcObjectFiles:        []string{"foo.o"},
+				Includes:             []string{"include"},
+				SystemIncludes:       []string{"system_include"},
+				RootDynamicLibraries: []string{"foo.so"},
+				TocFile:              "foo.so.toc",
+			},
+		},
+	}
+	ctx := testCcWithConfig(t, config)
+
+	sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+	producer := sharedFoo.(android.OutputFileProducer)
+	outputFiles, err := producer.OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.so"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+	tocFilePath := sharedFoo.(*Module).Toc()
+	if !tocFilePath.Valid() {
+		t.Errorf("Invalid tocFilePath: %s", tocFilePath)
+	}
+	tocFile := tocFilePath.Path()
+	expectedToc := "outputbase/execroot/__main__/foo.so.toc"
+	android.AssertStringEquals(t, "toc file", expectedToc, tocFile.String())
+
+	entries := android.AndroidMkEntriesForTest(t, ctx, sharedFoo)[0]
+	expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"}
+	gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+	android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
+}
diff --git a/cc/object.go b/cc/object.go
index 606e368..99b257a 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -122,12 +122,14 @@
 
 // For bp2build conversion.
 type bazelObjectAttributes struct {
-	Srcs    bazel.LabelListAttribute
-	Srcs_as bazel.LabelListAttribute
-	Hdrs    bazel.LabelListAttribute
-	Deps    bazel.LabelListAttribute
-	Copts   bazel.StringListAttribute
-	Asflags bazel.StringListAttribute
+	Srcs              bazel.LabelListAttribute
+	Srcs_as           bazel.LabelListAttribute
+	Hdrs              bazel.LabelListAttribute
+	Deps              bazel.LabelListAttribute
+	Copts             bazel.StringListAttribute
+	Asflags           bazel.StringListAttribute
+	Local_includes    bazel.StringListAttribute
+	Absolute_includes bazel.StringListAttribute
 }
 
 // ObjectBp2Build is the bp2build converter from cc_object modules to the
@@ -170,11 +172,13 @@
 	}
 
 	attrs := &bazelObjectAttributes{
-		Srcs:    srcs,
-		Srcs_as: compilerAttrs.asSrcs,
-		Deps:    deps,
-		Copts:   compilerAttrs.copts,
-		Asflags: asFlags,
+		Srcs:              srcs,
+		Srcs_as:           compilerAttrs.asSrcs,
+		Deps:              deps,
+		Copts:             compilerAttrs.copts,
+		Asflags:           asFlags,
+		Local_includes:    compilerAttrs.localIncludes,
+		Absolute_includes: compilerAttrs.absoluteIncludes,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/test.go b/cc/test.go
index 047a69e..3934784 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -357,8 +357,7 @@
 }
 
 func (test *testBinary) install(ctx ModuleContext, file android.Path) {
-	// TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base.
-	testInstallBase := "/data/local/tmp"
+	testInstallBase := "/data/local/tests/unrestricted"
 	if ctx.inVendor() || ctx.useVndk() {
 		testInstallBase = "/data/local/tests/vendor"
 	}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index ba4d79f..8a17e2e 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -132,12 +132,9 @@
 	return false
 }
 
-// This is to be saved as .json files, which is for development/vendor_snapshot/update.py.
-// These flags become Android.bp snapshot module properties.
+// Extend the snapshot.SnapshotJsonFlags to include cc specific fields.
 type snapshotJsonFlags struct {
-	ModuleName          string `json:",omitempty"`
-	RelativeInstallPath string `json:",omitempty"`
-
+	snapshot.SnapshotJsonFlags
 	// library flags
 	ExportedDirs       []string `json:",omitempty"`
 	ExportedSystemDirs []string `json:",omitempty"`
@@ -154,7 +151,6 @@
 	SharedLibs  []string `json:",omitempty"`
 	StaticLibs  []string `json:",omitempty"`
 	RuntimeLibs []string `json:",omitempty"`
-	Required    []string `json:",omitempty"`
 
 	// extra config files
 	InitRc         []string `json:",omitempty"`
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index 5770a7f..fe567a9 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -406,6 +406,7 @@
         "{{.}}",
         {{- end}}
     ],
+    {{- end}}
     {{- if .BpOptionalUsesLibs}}
     optional_uses_libs: [
         {{- range .BpOptionalUsesLibs}}
@@ -455,12 +456,6 @@
         "{{.}}",
         {{- end}}
     ],
-    {{- if .BpOptionalUsesLibs}}
-    optional_uses_libs: [
-        {{- range .BpOptionalUsesLibs}}
-        "{{.}}",
-        {{- end}}
-    ],
     {{- end}}
     {{- else if not .IsHostOnly}}
     min_sdk_version: "{{.DefaultMinSdkVersion}}",
@@ -505,6 +500,7 @@
         "{{.}}",
         {{- end}}
     ],
+    {{- end}}
     {{- if .BpOptionalUsesLibs}}
     optional_uses_libs: [
         {{- range .BpOptionalUsesLibs}}
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index 98e27c6..d63ded5 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -27,12 +27,12 @@
 	os.RemoveAll(bazelQueryViewDir)
 	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
 
-	// Ignore metrics reporting and compat layers for queryview, since queryview
-	// is already a full-repo conversion and can use data from bazel query
-	// directly.
-	buildToTargets, _, _ := bp2build.GenerateBazelTargets(ctx, true)
+	res, err := bp2build.GenerateBazelTargets(ctx, true)
+	if err != nil {
+		panic(err)
+	}
 
-	filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView)
+	filesToWrite := bp2build.CreateBazelFiles(ruleShims, res.BuildDirToTargets(), bp2build.QueryView)
 	for _, f := range filesToWrite {
 		if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
 			return err
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 1401c75..7733c1b 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -110,17 +110,12 @@
 		return true
 	}
 
-	// Don't preopt system server jars that are updatable.
-	if global.ApexSystemServerJars.ContainsJar(module.Name) {
-		return true
-	}
-
 	// If OnlyPreoptBootImageAndSystemServer=true and module is not in boot class path skip
 	// Also preopt system server jars since selinux prevents system server from loading anything from
 	// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
 	// or performance. If PreoptExtractedApk is true, we ignore the only preopt boot image options.
 	if global.OnlyPreoptBootImageAndSystemServer && !global.BootJars.ContainsJar(module.Name) &&
-		!global.SystemServerJars.ContainsJar(module.Name) && !module.PreoptExtractedApk {
+		!AllSystemServerJars(ctx, global).ContainsJar(module.Name) && !module.PreoptExtractedApk {
 		return true
 	}
 
@@ -201,6 +196,14 @@
 	return profilePath
 }
 
+// Returns the dex location of a system server java library.
+func GetSystemServerDexLocation(global *GlobalConfig, lib string) string {
+	if apex := global.ApexSystemServerJars.ApexOfJar(lib); apex != "" {
+		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apex, lib)
+	}
+	return fmt.Sprintf("/system/framework/%s.jar", lib)
+}
+
 func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
 	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
 	appImage bool, generateDM bool) {
@@ -216,6 +219,13 @@
 	}
 
 	toOdexPath := func(path string) string {
+		if global.ApexSystemServerJars.ContainsJar(module.Name) {
+			return filepath.Join(
+				"/system/framework/oat",
+				arch.String(),
+				strings.ReplaceAll(path[1:], "/", "@")+"@classes.odex")
+		}
+
 		return filepath.Join(
 			filepath.Dir(path),
 			"oat",
@@ -234,20 +244,21 @@
 
 	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
 
-	systemServerJars := NonApexSystemServerJars(ctx, global)
+	systemServerJars := AllSystemServerJars(ctx, global)
 
 	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
 	rule.Command().FlagWithOutput("rm -f ", odexPath)
 
-	if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
+	if jarIndex := systemServerJars.IndexOfJar(module.Name); jarIndex >= 0 {
 		// System server jars should be dexpreopted together: class loader context of each jar
 		// should include all preceding jars on the system server classpath.
 
 		var clcHost android.Paths
 		var clcTarget []string
-		for _, lib := range systemServerJars[:jarIndex] {
+		for i := 0; i < jarIndex; i++ {
+			lib := systemServerJars.Jar(i)
 			clcHost = append(clcHost, SystemServerDexJarHostPath(ctx, lib))
-			clcTarget = append(clcTarget, filepath.Join("/system/framework", lib+".jar"))
+			clcTarget = append(clcTarget, GetSystemServerDexLocation(global, lib))
 		}
 
 		// Copy the system server jar to a predefined location where dex2oat will find it.
@@ -362,7 +373,7 @@
 
 	if !android.PrefixInList(preoptFlags, "--compiler-filter=") {
 		var compilerFilter string
-		if global.SystemServerJars.ContainsJar(module.Name) {
+		if systemServerJars.ContainsJar(module.Name) {
 			// Jars of system server, use the product option if it is set, speed otherwise.
 			if global.SystemServerCompilerFilter != "" {
 				compilerFilter = global.SystemServerCompilerFilter
@@ -416,7 +427,7 @@
 
 	// PRODUCT_SYSTEM_SERVER_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
 	// PRODUCT_OTHER_JAVA_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
-	if global.SystemServerJars.ContainsJar(module.Name) {
+	if systemServerJars.ContainsJar(module.Name) {
 		if global.AlwaysSystemServerDebugInfo {
 			debugInfo = true
 		} else if global.NeverSystemServerDebugInfo {
@@ -518,14 +529,15 @@
 	}
 }
 
-var nonApexSystemServerJarsKey = android.NewOnceKey("nonApexSystemServerJars")
+var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars")
 
 // TODO: eliminate the superficial global config parameter by moving global config definition
 // from java subpackage to dexpreopt.
-func NonApexSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
-	return ctx.Config().Once(nonApexSystemServerJarsKey, func() interface{} {
-		return android.RemoveListFromList(global.SystemServerJars.CopyOfJars(), global.ApexSystemServerJars.CopyOfJars())
-	}).([]string)
+func AllSystemServerJars(ctx android.PathContext, global *GlobalConfig) *android.ConfiguredJarList {
+	return ctx.Config().Once(allSystemServerJarsKey, func() interface{} {
+		allSystemServerJars := global.SystemServerJars.AppendList(global.ApexSystemServerJars)
+		return &allSystemServerJars
+	}).(*android.ConfiguredJarList)
 }
 
 // A predefined location for the system server dex jars. This is needed in order to generate
@@ -551,12 +563,12 @@
 	mctx, isModule := ctx.(android.ModuleContext)
 	if isModule {
 		config := GetGlobalConfig(ctx)
-		jars := NonApexSystemServerJars(ctx, config)
+		jars := AllSystemServerJars(ctx, config)
 		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
-			depIndex := android.IndexList(dep.Name(), jars)
+			depIndex := jars.IndexOfJar(dep.Name())
 			if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
-				jar := jars[jarIndex]
-				dep := jars[depIndex]
+				jar := jars.Jar(jarIndex)
+				dep := jars.Jar(depIndex)
 				mctx.ModuleErrorf("non-optimal order of jars on the system server classpath:"+
 					" '%s' precedes its dependency '%s', so dexpreopt is unable to resolve any"+
 					" references from '%s' to '%s'.\n", jar, dep, jar, dep)
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 4ee61b6..798d776 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -33,17 +33,35 @@
 }
 
 func testModuleConfig(ctx android.PathContext, name, partition string) *ModuleConfig {
+	return createTestModuleConfig(
+		name,
+		fmt.Sprintf("/%s/app/test/%s.apk", partition, name),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/%s.apk", name, name)),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/dex/%s.jar", name, name)),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
+}
+
+func testApexModuleConfig(ctx android.PathContext, name, apexName string) *ModuleConfig {
+	return createTestModuleConfig(
+		name,
+		fmt.Sprintf("/apex/%s/javalib/%s.jar", apexName, name),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/dexpreopt/%s.jar", name, name)),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/aligned/%s.jar", name, name)),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
+}
+
+func createTestModuleConfig(name, dexLocation string, buildPath, dexPath, enforceUsesLibrariesStatusFile android.OutputPath) *ModuleConfig {
 	return &ModuleConfig{
 		Name:                            name,
-		DexLocation:                     fmt.Sprintf("/%s/app/test/%s.apk", partition, name),
-		BuildPath:                       android.PathForOutput(ctx, fmt.Sprintf("%s/%s.apk", name, name)),
-		DexPath:                         android.PathForOutput(ctx, fmt.Sprintf("%s/dex/%s.jar", name, name)),
+		DexLocation:                     dexLocation,
+		BuildPath:                       buildPath,
+		DexPath:                         dexPath,
 		UncompressedDex:                 false,
 		HasApkLibraries:                 false,
 		PreoptFlags:                     nil,
 		ProfileClassListing:             android.OptionalPath{},
 		ProfileIsTextListing:            false,
-		EnforceUsesLibrariesStatusFile:  android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)),
+		EnforceUsesLibrariesStatusFile:  enforceUsesLibrariesStatusFile,
 		EnforceUsesLibraries:            false,
 		ClassLoaderContexts:             nil,
 		Archs:                           []android.ArchType{android.Arm},
@@ -140,6 +158,29 @@
 
 }
 
+func TestDexPreoptApexSystemServerJars(t *testing.T) {
+	config := android.TestConfig("out", nil, "", nil)
+	ctx := android.BuilderContextForTesting(config)
+	globalSoong := globalSoongConfigForTests()
+	global := GlobalConfigForTests(ctx)
+	module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
+
+	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
+		[]string{"com.android.apex1:service-A"})
+
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	wantInstalls := android.RuleBuilderInstalls{
+		{android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.odex"), "/system/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.odex"},
+		{android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.vdex"},
+	}
+
+	android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
+}
+
 func TestDexPreoptProfile(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 3213e5c..85abf59 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -519,13 +519,6 @@
 	return module
 }
 
-// Flags to be included in the snapshot
-type snapshotJsonFlags struct {
-	ModuleName          string `json:",omitempty"`
-	Filename            string `json:",omitempty"`
-	RelativeInstallPath string `json:",omitempty"`
-}
-
 // Copy file into the snapshot
 func copyFile(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath {
 	if fake {
@@ -612,7 +605,7 @@
 		snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "etc", m.BaseModuleName())
 		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, m.OutputFile(), snapshotLibOut, s.Fake))
 
-		prop := snapshotJsonFlags{}
+		prop := snapshot.SnapshotJsonFlags{}
 		propOut := snapshotLibOut + ".json"
 		prop.ModuleName = m.BaseModuleName()
 		if m.subdirProperties.Relative_install_path != nil {
diff --git a/java/aar.go b/java/aar.go
index afbaea2..13390db 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -486,6 +486,18 @@
 	exportedStaticPackages    android.Paths
 }
 
+var _ android.OutputFileProducer = (*AndroidLibrary)(nil)
+
+// For OutputFileProducer interface
+func (a *AndroidLibrary) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case ".aar":
+		return []android.Path{a.aarFile}, nil
+	default:
+		return a.Library.OutputFiles(tag)
+	}
+}
+
 func (a *AndroidLibrary) ExportedProguardFlagFiles() android.Paths {
 	return a.exportedProguardFlagFiles
 }
diff --git a/java/androidmk.go b/java/androidmk.go
index 68ccd82..71370c9 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -61,7 +61,13 @@
 	var entriesList []android.AndroidMkEntries
 
 	if library.hideApexVariantFromMake {
-		// For a java library built for an APEX we don't need Make module
+		// For a java library built for an APEX, we don't need a Make module for itself. Otherwise, it
+		// will conflict with the platform variant because they have the same module name in the
+		// makefile. However, we need to add its dexpreopt outputs as sub-modules, if it is preopted.
+		dexpreoptEntries := library.dexpreopter.AndroidMkEntriesForApex()
+		if len(dexpreoptEntries) > 0 {
+			entriesList = append(entriesList, dexpreoptEntries...)
+		}
 		entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true})
 	} else if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) {
 		// Platform variant.  If not available for the platform, we don't need Make module.
diff --git a/java/app.go b/java/app.go
index 5104f07..a62e442 100755
--- a/java/app.go
+++ b/java/app.go
@@ -760,18 +760,18 @@
 				}
 
 				lib := dep.OutputFile()
-				path := lib.Path()
-				if seenModulePaths[path.String()] {
-					return false
-				}
-				seenModulePaths[path.String()] = true
-
-				if checkNativeSdkVersion && dep.SdkVersion() == "" {
-					ctx.PropertyErrorf("jni_libs", "JNI dependency %q uses platform APIs, but this module does not",
-						otherName)
-				}
-
 				if lib.Valid() {
+					path := lib.Path()
+					if seenModulePaths[path.String()] {
+						return false
+					}
+					seenModulePaths[path.String()] = true
+
+					if checkNativeSdkVersion && dep.SdkVersion() == "" {
+						ctx.PropertyErrorf("jni_libs", "JNI dependency %q uses platform APIs, but this module does not",
+							otherName)
+					}
+
 					jniLibs = append(jniLibs, jniLib{
 						name:           ctx.OtherModuleName(module),
 						path:           path,
diff --git a/java/base.go b/java/base.go
index 86022c3..da9293c 100644
--- a/java/base.go
+++ b/java/base.go
@@ -382,7 +382,7 @@
 		return nil
 	}
 	if sdkVersion.Kind == android.SdkCorePlatform {
-		if useLegacyCorePlatformApiByName(j.BaseModuleName()) {
+		if useLegacyCorePlatformApi(ctx, j.BaseModuleName()) {
 			return fmt.Errorf("non stable SDK %v - uses legacy core platform", sdkVersion)
 		} else {
 			// Treat stable core platform as stable.
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index 4108770..52ce77d 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -95,15 +95,6 @@
 	if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) {
 		ctx.AddVariationDependencies(variations, tag, prebuiltName)
 		addedDep = true
-	} else if ctx.Config().AlwaysUsePrebuiltSdks() && len(variations) > 0 {
-		// TODO(b/179354495): Remove this code path once the Android build has been fully migrated to
-		//  use bootclasspath_fragment properly.
-		// Some prebuilt java_sdk_library modules do not yet have an APEX variations so try and add a
-		// dependency on the non-APEX variant.
-		if ctx.OtherModuleDependencyVariantExists(nil, prebuiltName) {
-			ctx.AddVariationDependencies(nil, tag, prebuiltName)
-			addedDep = true
-		}
 	}
 
 	// If no appropriate variant existing for this, so no dependency could be added, then it is an
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index f7561b4..b13e2db 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -89,7 +89,7 @@
 
 var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathFragmentContentDepTag
 var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag
-var _ android.SdkMemberTypeDependencyTag = bootclasspathFragmentContentDepTag
+var _ android.SdkMemberDependencyTag = bootclasspathFragmentContentDepTag
 var _ android.CopyDirectlyInAnyApexTag = bootclasspathFragmentContentDepTag
 var _ android.RequiresFilesFromPrebuiltApexTag = bootclasspathFragmentContentDepTag
 
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 0faae36..e9dc982 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -15,13 +15,46 @@
 package java
 
 import (
+	"path/filepath"
+	"strings"
+
 	"android/soong/android"
 	"android/soong/dexpreopt"
 )
 
-type dexpreopterInterface interface {
+type DexpreopterInterface interface {
 	IsInstallable() bool // Structs that embed dexpreopter must implement this.
 	dexpreoptDisabled(ctx android.BaseModuleContext) bool
+	DexpreoptBuiltInstalledForApex() []dexpreopterInstall
+	AndroidMkEntriesForApex() []android.AndroidMkEntries
+}
+
+type dexpreopterInstall struct {
+	// A unique name to distinguish an output from others for the same java library module. Usually in
+	// the form of `<arch>-<encoded-path>.odex/vdex/art`.
+	name string
+
+	// The name of the input java module.
+	moduleName string
+
+	// The path to the dexpreopt output on host.
+	outputPathOnHost android.Path
+
+	// The directory on the device for the output to install to.
+	installDirOnDevice android.InstallPath
+
+	// The basename (the last segment of the path) for the output to install as.
+	installFileOnDevice string
+}
+
+// The full module name of the output in the makefile.
+func (install *dexpreopterInstall) FullModuleName() string {
+	return install.moduleName + install.SubModuleName()
+}
+
+// The sub-module name of the output in the makefile (the name excluding the java module name).
+func (install *dexpreopterInstall) SubModuleName() string {
+	return "-dexpreopt-" + install.name
 }
 
 type dexpreopter struct {
@@ -39,7 +72,9 @@
 	enforceUsesLibs     bool
 	classLoaderContexts dexpreopt.ClassLoaderContextMap
 
-	builtInstalled string
+	// See the `dexpreopt` function for details.
+	builtInstalled        string
+	builtInstalledForApex []dexpreopterInstall
 
 	// The config is used for two purposes:
 	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
@@ -74,6 +109,17 @@
 	dexpreopt.DexpreoptRunningInSoong = true
 }
 
+func isApexVariant(ctx android.BaseModuleContext) bool {
+	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	return !apexInfo.IsForPlatform()
+}
+
+func moduleName(ctx android.BaseModuleContext) string {
+	// Remove the "prebuilt_" prefix if the module is from a prebuilt because the prefix is not
+	// expected by dexpreopter.
+	return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
+}
+
 func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
 	global := dexpreopt.GetGlobalConfig(ctx)
 
@@ -81,7 +127,7 @@
 		return true
 	}
 
-	if inList(ctx.ModuleName(), global.DisablePreoptModules) {
+	if inList(moduleName(ctx), global.DisablePreoptModules) {
 		return true
 	}
 
@@ -93,7 +139,7 @@
 		return true
 	}
 
-	if !ctx.Module().(dexpreopterInterface).IsInstallable() {
+	if !ctx.Module().(DexpreopterInterface).IsInstallable() {
 		return true
 	}
 
@@ -101,8 +147,20 @@
 		return true
 	}
 
-	// Don't preopt APEX variant module
-	if apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo); !apexInfo.IsForPlatform() {
+	if isApexVariant(ctx) {
+		// Don't preopt APEX variant module unless the module is an APEX system server jar and we are
+		// building the entire system image.
+		if !global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) || ctx.Config().UnbundledBuild() {
+			return true
+		}
+	} else {
+		// Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
+		if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
+			return true
+		}
+	}
+
+	if !android.IsModulePreferred(ctx.Module()) {
 		return true
 	}
 
@@ -112,17 +170,40 @@
 }
 
 func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
-	if d, ok := ctx.Module().(dexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
+	if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
 		return
 	}
 	dexpreopt.RegisterToolDeps(ctx)
 }
 
-func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
-	return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
+func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
+	return dexpreopt.OdexOnSystemOtherByName(moduleName(ctx), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
+}
+
+// Returns the install path of the dex jar of a module.
+//
+// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather
+// than the `name` in the path `/apex/<name>` as suggested in its comment.
+//
+// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a
+// system server jar, which is fine because we currently only preopt system server jars for APEXes.
+func (d *dexpreopter) getInstallPath(
+	ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
+		dexLocation := dexpreopt.GetSystemServerDexLocation(global, moduleName(ctx))
+		return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/"))
+	}
+	if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) &&
+		filepath.Base(defaultInstallPath.PartitionDir()) != "apex" {
+		ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt")
+	}
+	return defaultInstallPath
 }
 
 func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
+	global := dexpreopt.GetGlobalConfig(ctx)
+
 	// TODO(b/148690468): The check on d.installPath is to bail out in cases where
 	// the dexpreopter struct hasn't been fully initialized before we're called,
 	// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
@@ -133,7 +214,7 @@
 
 	dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
 
-	providesUsesLib := ctx.ModuleName()
+	providesUsesLib := moduleName(ctx)
 	if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
 		name := ulib.ProvidesUsesLib()
 		if name != nil {
@@ -147,17 +228,15 @@
 		return
 	}
 
-	global := dexpreopt.GetGlobalConfig(ctx)
-
-	isSystemServerJar := global.SystemServerJars.ContainsJar(ctx.ModuleName())
+	isSystemServerJar := global.SystemServerJars.ContainsJar(moduleName(ctx)) ||
+		global.ApexSystemServerJars.ContainsJar(moduleName(ctx))
 
 	bootImage := defaultBootImageConfig(ctx)
 	if global.UseArtImage {
 		bootImage = artBootImageConfig(ctx)
 	}
 
-	// System server jars are an exception: they are dexpreopted without updatable bootclasspath.
-	dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp && !isSystemServerJar)
+	dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
 
 	targets := ctx.MultiTargets()
 	if len(targets) == 0 {
@@ -199,15 +278,15 @@
 			profileIsTextListing = true
 		} else if global.ProfileDir != "" {
 			profileClassListing = android.ExistentPathForSource(ctx,
-				global.ProfileDir, ctx.ModuleName()+".prof")
+				global.ProfileDir, moduleName(ctx)+".prof")
 		}
 	}
 
 	// Full dexpreopt config, used to create dexpreopt build rules.
 	dexpreoptConfig := &dexpreopt.ModuleConfig{
-		Name:            ctx.ModuleName(),
+		Name:            moduleName(ctx),
 		DexLocation:     dexLocation,
-		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath,
+		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath,
 		DexPath:         dexJarFile,
 		ManifestPath:    android.OptionalPathForPath(d.manifestFile),
 		UncompressedDex: d.uncompressedDex,
@@ -256,5 +335,53 @@
 
 	dexpreoptRule.Build("dexpreopt", "dexpreopt")
 
-	d.builtInstalled = dexpreoptRule.Installs().String()
+	if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
+		// APEX variants of java libraries are hidden from Make, so their dexpreopt outputs need special
+		// handling. Currently, for APEX variants of java libraries, only those in the system server
+		// classpath are handled here. Preopting of boot classpath jars in the ART APEX are handled in
+		// java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
+		for _, install := range dexpreoptRule.Installs() {
+			// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
+			installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
+			installBase := filepath.Base(install.To)
+			arch := filepath.Base(installDir)
+			installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+			// The installs will be handled by Make as sub-modules of the java library.
+			d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
+				name:                arch + "-" + installBase,
+				moduleName:          moduleName(ctx),
+				outputPathOnHost:    install.From,
+				installDirOnDevice:  installPath,
+				installFileOnDevice: installBase,
+			})
+		}
+	} else {
+		// The installs will be handled by Make as LOCAL_SOONG_BUILT_INSTALLED of the java library
+		// module.
+		d.builtInstalled = dexpreoptRule.Installs().String()
+	}
+}
+
+func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall {
+	return d.builtInstalledForApex
+}
+
+func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
+	var entries []android.AndroidMkEntries
+	for _, install := range d.builtInstalledForApex {
+		install := install
+		entries = append(entries, android.AndroidMkEntries{
+			Class:      "ETC",
+			SubName:    install.SubModuleName(),
+			OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
+			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+					entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.ToMakePath().String())
+					entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
+					entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
+				},
+			},
+		})
+	}
+	return entries
 }
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 8dc7b79..1c1070a 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"runtime"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -24,11 +25,17 @@
 	"android/soong/dexpreopt"
 )
 
+func init() {
+	RegisterFakeRuntimeApexMutator()
+}
+
 func TestDexpreoptEnabled(t *testing.T) {
 	tests := []struct {
-		name    string
-		bp      string
-		enabled bool
+		name        string
+		bp          string
+		moduleName  string
+		apexVariant bool
+		enabled     bool
 	}{
 		{
 			name: "app",
@@ -148,13 +155,81 @@
 				}`,
 			enabled: true,
 		},
+		{
+			name: "apex variant",
+			bp: `
+				java_library {
+					name: "foo",
+					installable: true,
+					srcs: ["a.java"],
+					apex_available: ["com.android.apex1"],
+				}`,
+			apexVariant: true,
+			enabled:     false,
+		},
+		{
+			name: "apex variant of apex system server jar",
+			bp: `
+				java_library {
+					name: "service-foo",
+					installable: true,
+					srcs: ["a.java"],
+					apex_available: ["com.android.apex1"],
+				}`,
+			moduleName:  "service-foo",
+			apexVariant: true,
+			enabled:     true,
+		},
+		{
+			name: "apex variant of prebuilt apex system server jar",
+			bp: `
+				java_library {
+					name: "prebuilt_service-foo",
+					installable: true,
+					srcs: ["a.java"],
+					apex_available: ["com.android.apex1"],
+				}`,
+			moduleName:  "prebuilt_service-foo",
+			apexVariant: true,
+			enabled:     true,
+		},
+		{
+			name: "platform variant of apex system server jar",
+			bp: `
+				java_library {
+					name: "service-foo",
+					installable: true,
+					srcs: ["a.java"],
+					apex_available: ["com.android.apex1"],
+				}`,
+			moduleName:  "service-foo",
+			apexVariant: false,
+			enabled:     false,
+		},
 	}
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			ctx, _ := testJava(t, test.bp)
+			preparers := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				PrepareForTestWithFakeApexMutator,
+				dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
+			)
 
-			dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
+			result := preparers.RunTestWithBp(t, test.bp)
+			ctx := result.TestContext
+
+			moduleName := "foo"
+			if test.moduleName != "" {
+				moduleName = test.moduleName
+			}
+
+			variant := "android_common"
+			if test.apexVariant {
+				variant += "_apex1000"
+			}
+
+			dexpreopt := ctx.ModuleForTests(moduleName, variant).MaybeRule("dexpreopt")
 			enabled := dexpreopt.Rule != nil
 
 			if enabled != test.enabled {
@@ -220,3 +295,145 @@
 	testDex2oatToolDep(true, true, true, prebuiltDex2oatPath)
 	testDex2oatToolDep(false, true, false, prebuiltDex2oatPath)
 }
+
+func TestDexpreoptBuiltInstalledForApex(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithFakeApexMutator,
+		dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
+	)
+
+	// An APEX system server jar.
+	result := preparers.RunTestWithBp(t, `
+		java_library {
+			name: "service-foo",
+			installable: true,
+			srcs: ["a.java"],
+			apex_available: ["com.android.apex1"],
+		}`)
+	ctx := result.TestContext
+	module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
+	library := module.Module().(*Library)
+
+	installs := library.dexpreopter.DexpreoptBuiltInstalledForApex()
+
+	android.AssertIntEquals(t, "install count", 2, len(installs))
+
+	android.AssertStringEquals(t, "installs[0] FullModuleName",
+		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+		installs[0].FullModuleName())
+
+	android.AssertStringEquals(t, "installs[0] SubModuleName",
+		"-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+		installs[0].SubModuleName())
+
+	android.AssertStringEquals(t, "installs[1] FullModuleName",
+		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+		installs[1].FullModuleName())
+
+	android.AssertStringEquals(t, "installs[1] SubModuleName",
+		"-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+		installs[1].SubModuleName())
+
+	// Not an APEX system server jar.
+	result = preparers.RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			installable: true,
+			srcs: ["a.java"],
+		}`)
+	ctx = result.TestContext
+	module = ctx.ModuleForTests("foo", "android_common")
+	library = module.Module().(*Library)
+
+	installs = library.dexpreopter.DexpreoptBuiltInstalledForApex()
+
+	android.AssertIntEquals(t, "install count", 0, len(installs))
+}
+
+func filterDexpreoptEntriesList(entriesList []android.AndroidMkEntries) []android.AndroidMkEntries {
+	var results []android.AndroidMkEntries
+	for _, entries := range entriesList {
+		if strings.Contains(entries.EntryMap["LOCAL_MODULE"][0], "-dexpreopt-") {
+			results = append(results, entries)
+		}
+	}
+	return results
+}
+
+func verifyEntries(t *testing.T, message string, expectedModule string,
+	expectedPrebuiltModuleFile string, expectedModulePath string, expectedInstalledModuleStem string,
+	entries android.AndroidMkEntries) {
+	android.AssertStringEquals(t, message+" LOCAL_MODULE", expectedModule,
+		entries.EntryMap["LOCAL_MODULE"][0])
+
+	android.AssertStringEquals(t, message+" LOCAL_MODULE_CLASS", "ETC",
+		entries.EntryMap["LOCAL_MODULE_CLASS"][0])
+
+	android.AssertStringDoesContain(t, message+" LOCAL_PREBUILT_MODULE_FILE",
+		entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"][0], expectedPrebuiltModuleFile)
+
+	android.AssertStringDoesContain(t, message+" LOCAL_MODULE_PATH",
+		entries.EntryMap["LOCAL_MODULE_PATH"][0], expectedModulePath)
+
+	android.AssertStringEquals(t, message+" LOCAL_INSTALLED_MODULE_STEM",
+		expectedInstalledModuleStem, entries.EntryMap["LOCAL_INSTALLED_MODULE_STEM"][0])
+
+	android.AssertStringEquals(t, message+" LOCAL_NOT_AVAILABLE_FOR_PLATFORM",
+		"false", entries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"][0])
+}
+
+func TestAndroidMkEntriesForApex(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithFakeApexMutator,
+		dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
+	)
+
+	// An APEX system server jar.
+	result := preparers.RunTestWithBp(t, `
+		java_library {
+			name: "service-foo",
+			installable: true,
+			srcs: ["a.java"],
+			apex_available: ["com.android.apex1"],
+		}`)
+	ctx := result.TestContext
+	module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
+
+	entriesList := android.AndroidMkEntriesForTest(t, ctx, module.Module())
+	entriesList = filterDexpreoptEntriesList(entriesList)
+
+	android.AssertIntEquals(t, "entries count", 2, len(entriesList))
+
+	verifyEntries(t,
+		"entriesList[0]",
+		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+		"/dexpreopt/oat/arm64/javalib.odex",
+		"/system/framework/oat/arm64",
+		"apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+		entriesList[0])
+
+	verifyEntries(t,
+		"entriesList[1]",
+		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+		"/dexpreopt/oat/arm64/javalib.vdex",
+		"/system/framework/oat/arm64",
+		"apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+		entriesList[1])
+
+	// Not an APEX system server jar.
+	result = preparers.RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			installable: true,
+			srcs: ["a.java"],
+		}`)
+	ctx = result.TestContext
+	module = ctx.ModuleForTests("foo", "android_common")
+
+	entriesList = android.AndroidMkEntriesForTest(t, ctx, module.Module())
+	entriesList = filterDexpreoptEntriesList(entriesList)
+
+	android.AssertIntEquals(t, "entries count", 0, len(entriesList))
+}
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 8e39f40..51cd501 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -218,7 +218,7 @@
 var _ android.ExcludeFromVisibilityEnforcementTag = hiddenAPIStubsDependencyTag{}
 var _ android.ReplaceSourceWithPrebuilt = hiddenAPIStubsDependencyTag{}
 var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{}
-var _ android.SdkMemberTypeDependencyTag = hiddenAPIStubsDependencyTag{}
+var _ android.SdkMemberDependencyTag = hiddenAPIStubsDependencyTag{}
 
 // hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs
 // needed to produce the hidden API monolithic stub flags file.
diff --git a/java/java.go b/java/java.go
index 1a052b4..e2665ef 100644
--- a/java/java.go
+++ b/java/java.go
@@ -487,7 +487,7 @@
 	}
 
 	// Store uncompressed dex files that are preopted on /system.
-	if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !odexOnSystemOther(ctx, dexpreopter.installPath)) {
+	if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !dexpreopter.odexOnSystemOther(ctx, dexpreopter.installPath)) {
 		return true
 	}
 	if ctx.Config().UncompressPrivAppDex() &&
@@ -508,7 +508,8 @@
 	}
 
 	j.checkSdkVersions(ctx)
-	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
+	j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
+		ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
 	j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
 	if j.dexProperties.Uncompress_dex == nil {
 		// If the value was not force-set by the user, use reasonable default based on the module.
@@ -1368,7 +1369,8 @@
 
 			// Dex compilation
 
-			j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
+			j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
+				ctx, android.PathForModuleInstall(ctx, "framework", jarName))
 			if j.dexProperties.Uncompress_dex == nil {
 				// If the value was not force-set by the user, use reasonable default based on the module.
 				j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
@@ -1509,7 +1511,7 @@
 	return Bool(j.properties.Installable)
 }
 
-var _ dexpreopterInterface = (*Import)(nil)
+var _ DexpreopterInterface = (*Import)(nil)
 
 // java_import imports one or more `.jar` files into the build graph as if they were built by a java_library module.
 //
@@ -1622,7 +1624,8 @@
 		j.hideApexVariantFromMake = true
 	}
 
-	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
+	j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
+		ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
 	j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter)
 
 	inputJar := ctx.ExpandSource(j.properties.Jars[0], "jars")
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 8c401a7..7749310 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -163,17 +163,27 @@
 	}
 }
 
-func useLegacyCorePlatformApi(ctx android.EarlyModuleContext) bool {
-	return useLegacyCorePlatformApiByName(ctx.ModuleName())
+var legacyCorePlatformApiLookupKey = android.NewOnceKey("legacyCorePlatformApiLookup")
+
+func getLegacyCorePlatformApiLookup(config android.Config) map[string]struct{} {
+	return config.Once(legacyCorePlatformApiLookupKey, func() interface{} {
+		return legacyCorePlatformApiLookup
+	}).(map[string]struct{})
 }
 
-func useLegacyCorePlatformApiByName(name string) bool {
-	_, found := legacyCorePlatformApiLookup[name]
+// useLegacyCorePlatformApi checks to see whether the supplied module name is in the list of modules
+// that are able to use the legacy core platform API and returns true if it does, false otherwise.
+//
+// This method takes the module name separately from the context as this may be being called for a
+// module that is not the target of the supplied context.
+func useLegacyCorePlatformApi(ctx android.EarlyModuleContext, moduleName string) bool {
+	lookup := getLegacyCorePlatformApiLookup(ctx.Config())
+	_, found := lookup[moduleName]
 	return found
 }
 
 func corePlatformSystemModules(ctx android.EarlyModuleContext) string {
-	if useLegacyCorePlatformApi(ctx) {
+	if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) {
 		return config.LegacyCorePlatformSystemModules
 	} else {
 		return config.StableCorePlatformSystemModules
@@ -181,7 +191,7 @@
 }
 
 func corePlatformBootclasspathLibraries(ctx android.EarlyModuleContext) []string {
-	if useLegacyCorePlatformApi(ctx) {
+	if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) {
 		return config.LegacyCorePlatformBootclasspathLibraries
 	} else {
 		return config.StableCorePlatformBootclasspathLibraries
diff --git a/java/sdk_library.go b/java/sdk_library.go
index ce8f179..1d39071 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1907,6 +1907,7 @@
 	android.SdkBase
 
 	hiddenAPI
+	dexpreopter
 
 	properties sdkLibraryImportProperties
 
@@ -2111,6 +2112,14 @@
 	}
 }
 
+func (module *SdkLibraryImport) AndroidMkEntries() []android.AndroidMkEntries {
+	// For an SDK library imported from a prebuilt APEX, we don't need a Make module for itself, as we
+	// don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it
+	// is preopted.
+	dexpreoptEntries := module.dexpreopter.AndroidMkEntriesForApex()
+	return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true})
+}
+
 var _ android.ApexModule = (*SdkLibraryImport)(nil)
 
 // Implements android.ApexModule
@@ -2208,8 +2217,16 @@
 			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()))
+				installPath := android.PathForModuleInPartitionInstall(
+					ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName()))
+				module.installFile = installPath
 				module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
+
+				// Dexpreopting.
+				module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, installPath)
+				module.dexpreopter.isSDKLibrary = true
+				module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &module.dexpreopter)
+				module.dexpreopt(ctx, dexOutputPath)
 			} else {
 				// This should never happen as a variant for a prebuilt_apex is only created if the
 				// prebuilt_apex has been configured to export the java library dex file.
@@ -2328,6 +2345,11 @@
 	}
 }
 
+// to satisfy java.DexpreopterInterface interface
+func (module *SdkLibraryImport) IsInstallable() bool {
+	return true
+}
+
 var _ android.RequiredFilesFromPrebuiltApex = (*SdkLibraryImport)(nil)
 
 func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 938bb28..d6c0946 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -598,6 +598,7 @@
 	}
 
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
+		`dex2oatd`,
 		`prebuilt_sdklib.stubs`,
 		`prebuilt_sdklib.stubs.source.test`,
 		`prebuilt_sdklib.stubs.system`,
@@ -674,7 +675,6 @@
 		`)
 
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
-		`dex2oatd`,
 		`prebuilt_sdklib`,
 		`sdklib.impl`,
 		`sdklib.stubs`,
@@ -683,6 +683,7 @@
 	})
 
 	CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
+		`dex2oatd`,
 		`prebuilt_sdklib.stubs`,
 		`sdklib.impl`,
 		`sdklib.xml`,
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 5311f62..de2a978 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -123,10 +123,16 @@
 	blueprint.BaseDependencyTag
 }
 
+// The systemserverclasspath_fragment contents must never depend on prebuilts.
+func (systemServerClasspathFragmentContentDependencyTag) ReplaceSourceWithPrebuilt() bool {
+	return false
+}
+
 // Contents of system server fragments in an apex are considered to be directly in the apex, as if
 // they were listed in java_libs.
 func (systemServerClasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {}
 
+var _ android.ReplaceSourceWithPrebuilt = systemServerClasspathFragmentContentDepTag
 var _ android.CopyDirectlyInAnyApexTag = systemServerClasspathFragmentContentDepTag
 
 // The tag used for the dependency between the systemserverclasspath_fragment module and its contents.
diff --git a/java/testing.go b/java/testing.go
index 8860b45..a642753 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -229,6 +229,26 @@
 	)
 }
 
+// FixtureUseLegacyCorePlatformApi prepares the fixture by setting the exception list of those
+// modules that are allowed to use the legacy core platform API to be the ones supplied.
+func FixtureUseLegacyCorePlatformApi(moduleNames ...string) android.FixturePreparer {
+	lookup := make(map[string]struct{})
+	for _, moduleName := range moduleNames {
+		lookup[moduleName] = struct{}{}
+	}
+	return android.FixtureModifyConfig(func(config android.Config) {
+		// Try and set the legacyCorePlatformApiLookup in the config, the returned value will be the
+		// actual value that is set.
+		cached := config.Once(legacyCorePlatformApiLookupKey, func() interface{} {
+			return lookup
+		})
+		// Make sure that the cached value is the one we need.
+		if !reflect.DeepEqual(cached, lookup) {
+			panic(fmt.Errorf("attempting to set legacyCorePlatformApiLookupKey to %q but it has already been set to %q", lookup, cached))
+		}
+	})
+}
+
 // registerRequiredBuildComponentsForTest registers the build components used by
 // PrepareForTestWithJavaDefaultModules.
 //
@@ -431,3 +451,45 @@
 	output := sourceGlobalCompatConfig.Output(allOutputs[0])
 	android.AssertPathsRelativeToTopEquals(t, message+": inputs", expectedPaths, output.Implicits)
 }
+
+// Register the fake APEX mutator to `android.InitRegistrationContext` as if the real mutator exists
+// at runtime. This must be called in `init()` of a test if the test is going to use the fake APEX
+// mutator. Otherwise, we will be missing the runtime mutator because "soong-apex" is not a
+// dependency, which will cause an inconsistency between testing and runtime mutators.
+func RegisterFakeRuntimeApexMutator() {
+	registerFakeApexMutator(android.InitRegistrationContext)
+}
+
+var PrepareForTestWithFakeApexMutator = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(registerFakeApexMutator),
+)
+
+func registerFakeApexMutator(ctx android.RegistrationContext) {
+	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("apex", fakeApexMutator).Parallel()
+	})
+}
+
+type apexModuleBase interface {
+	ApexAvailable() []string
+}
+
+var _ apexModuleBase = (*Library)(nil)
+var _ apexModuleBase = (*SdkLibrary)(nil)
+
+// A fake APEX mutator that creates a platform variant and an APEX variant for modules with
+// `apex_available`. It helps us avoid a dependency on the real mutator defined in "soong-apex",
+// which will cause a cyclic dependency, and it provides an easy way to create an APEX variant for
+// testing without dealing with all the complexities in the real mutator.
+func fakeApexMutator(mctx android.BottomUpMutatorContext) {
+	switch mctx.Module().(type) {
+	case *Library, *SdkLibrary:
+		if len(mctx.Module().(apexModuleBase).ApexAvailable()) > 0 {
+			modules := mctx.CreateVariations("", "apex1000")
+			apexInfo := android.ApexInfo{
+				ApexVariationName: "apex1000",
+			}
+			mctx.SetVariationProvider(modules[1], android.ApexInfoProvider, apexInfo)
+		}
+	}
+}
diff --git a/python/python.go b/python/python.go
index 83844e6..a35a1ac 100644
--- a/python/python.go
+++ b/python/python.go
@@ -310,13 +310,16 @@
 // HostToolPath returns a path if appropriate such that this module can be used as a host tool,
 // fulfilling HostToolProvider interface.
 func (p *Module) HostToolPath() android.OptionalPath {
-	if p.installer == nil {
-		// python_library is just meta module, and doesn't have any installer.
-		return android.OptionalPath{}
+	if p.installer != nil {
+		if bin, ok := p.installer.(*binaryDecorator); ok {
+			// TODO: This should only be set when building host binaries -- tests built for device would be
+			// setting this incorrectly.
+			return android.OptionalPathForPath(bin.path)
+		}
 	}
-	// TODO: This should only be set when building host binaries -- tests built for device would be
-	// setting this incorrectly.
-	return android.OptionalPathForPath(p.installer.(*binaryDecorator).path)
+
+	return android.OptionalPath{}
+
 }
 
 // OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
diff --git a/rust/bindgen.go b/rust/bindgen.go
index be9e71e..845f258 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r428724"
+	bindgenClangVersion = "clang-r433403"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 63a8f04..47ca3a7 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -28,6 +28,7 @@
 		"system/librustutils",
 		"system/logging/liblog",
 		"system/logging/rust",
+		"system/nfc",
 		"system/security",
 		"system/tools/aidl",
 		"tools/security/fuzzing/example_rust_fuzzer",
diff --git a/rust/config/global.go b/rust/config/global.go
index e5b334d..b163bb6 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.54.0"
+	RustDefaultVersion = "1.55.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/scripts/generate-notice-files.py b/scripts/generate-notice-files.py
index 49011b2..1b4acfa 100755
--- a/scripts/generate-notice-files.py
+++ b/scripts/generate-notice-files.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2012 The Android Open Source Project
 #
@@ -30,20 +30,18 @@
 import os
 import os.path
 import re
+import struct
 import sys
 
 MD5_BLOCKSIZE = 1024 * 1024
 HTML_ESCAPE_TABLE = {
-    "&": "&amp;",
-    '"': "&quot;",
-    "'": "&apos;",
-    ">": "&gt;",
-    "<": "&lt;",
+    b"&": b"&amp;",
+    b'"': b"&quot;",
+    b"'": b"&apos;",
+    b">": b"&gt;",
+    b"<": b"&lt;",
     }
 
-def hexify(s):
-    return ("%02x"*len(s)) % tuple(map(ord, s))
-
 def md5sum(filename):
     """Calculate an MD5 of the file given by FILENAME,
     and return hex digest as a string.
@@ -57,20 +55,26 @@
             break
         sum.update(block)
     f.close()
-    return hexify(sum.digest())
+    return sum.hexdigest()
 
 
 def html_escape(text):
     """Produce entities within text."""
-    return "".join(HTML_ESCAPE_TABLE.get(c,c) for c in text)
+    # Using for i in text doesn't work since i will be an int, not a byte.
+    # There are multiple ways to solve this, but the most performant way
+    # to iterate over a byte array is to use unpack. Using the
+    # for i in range(len(text)) and using that to get a byte using array
+    # slices is twice as slow as this method.
+    return b"".join(HTML_ESCAPE_TABLE.get(i,i) for i in struct.unpack(str(len(text)) + 'c', text))
 
-HTML_OUTPUT_CSS="""
+HTML_OUTPUT_CSS=b"""
 <style type="text/css">
 body { padding: 0; font-family: sans-serif; }
 .same-license { background-color: #eeeeee; border-top: 20px solid white; padding: 10px; }
 .label { font-weight: bold; }
 .file-list { margin-left: 1em; color: blue; }
 </style>
+
 """
 
 def combine_notice_files_html(file_hash, input_dir, output_filename):
@@ -90,13 +94,13 @@
     # Open the output file, and output the header pieces
     output_file = open(output_filename, "wb")
 
-    print >> output_file, "<html><head>"
-    print >> output_file, HTML_OUTPUT_CSS
-    print >> output_file, '</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">'
+    output_file.write(b"<html><head>\n")
+    output_file.write(HTML_OUTPUT_CSS)
+    output_file.write(b'</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">\n')
 
     # Output our table of contents
-    print >> output_file, '<div class="toc">'
-    print >> output_file, "<ul>"
+    output_file.write(b'<div class="toc">\n')
+    output_file.write(b"<ul>\n")
 
     # Flatten the list of lists into a single list of filenames
     sorted_filenames = sorted(itertools.chain.from_iterable(file_hash))
@@ -104,31 +108,28 @@
     # Print out a nice table of contents
     for filename in sorted_filenames:
         stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
-        print >> output_file, '<li><a href="#id%d">%s</a></li>' % (id_table.get(filename), stripped_filename)
+        output_file.write(('<li><a href="#id%d">%s</a></li>\n' % (id_table.get(filename), stripped_filename)).encode())
 
-    print >> output_file, "</ul>"
-    print >> output_file, "</div><!-- table of contents -->"
+    output_file.write(b"</ul>\n")
+    output_file.write(b"</div><!-- table of contents -->\n")
     # Output the individual notice file lists
-    print >>output_file, '<table cellpadding="0" cellspacing="0" border="0">'
+    output_file.write(b'<table cellpadding="0" cellspacing="0" border="0">\n')
     for value in file_hash:
-        print >> output_file, '<tr id="id%d"><td class="same-license">' % id_table.get(value[0])
-        print >> output_file, '<div class="label">Notices for file(s):</div>'
-        print >> output_file, '<div class="file-list">'
+        output_file.write(('<tr id="id%d"><td class="same-license">\n' % id_table.get(value[0])).encode())
+        output_file.write(b'<div class="label">Notices for file(s):</div>\n')
+        output_file.write(b'<div class="file-list">\n')
         for filename in value:
-            print >> output_file, "%s <br/>" % (SRC_DIR_STRIP_RE.sub(r"\1", filename))
-        print >> output_file, "</div><!-- file-list -->"
-        print >> output_file
-        print >> output_file, '<pre class="license-text">'
-        print >> output_file, html_escape(open(value[0]).read())
-        print >> output_file, "</pre><!-- license-text -->"
-        print >> output_file, "</td></tr><!-- same-license -->"
-        print >> output_file
-        print >> output_file
-        print >> output_file
+            output_file.write(("%s <br/>\n" % (SRC_DIR_STRIP_RE.sub(r"\1", filename))).encode())
+        output_file.write(b"</div><!-- file-list -->\n\n")
+        output_file.write(b'<pre class="license-text">\n')
+        with open(value[0], "rb") as notice_file:
+            output_file.write(html_escape(notice_file.read()))
+        output_file.write(b"\n</pre><!-- license-text -->\n")
+        output_file.write(b"</td></tr><!-- same-license -->\n\n\n\n")
 
     # Finish off the file output
-    print >> output_file, "</table>"
-    print >> output_file, "</body></html>"
+    output_file.write(b"</table>\n")
+    output_file.write(b"</body></html>\n")
     output_file.close()
 
 def combine_notice_files_text(file_hash, input_dir, output_filename, file_title):
@@ -136,14 +137,18 @@
 
     SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
     output_file = open(output_filename, "wb")
-    print >> output_file, file_title
+    output_file.write(file_title.encode())
+    output_file.write(b"\n")
     for value in file_hash:
-      print >> output_file, "============================================================"
-      print >> output_file, "Notices for file(s):"
-      for filename in value:
-        print >> output_file, SRC_DIR_STRIP_RE.sub(r"\1", filename)
-      print >> output_file, "------------------------------------------------------------"
-      print >> output_file, open(value[0]).read()
+        output_file.write(b"============================================================\n")
+        output_file.write(b"Notices for file(s):\n")
+        for filename in value:
+            output_file.write(SRC_DIR_STRIP_RE.sub(r"\1", filename).encode())
+            output_file.write(b"\n")
+        output_file.write(b"------------------------------------------------------------\n")
+        with open(value[0], "rb") as notice_file:
+            output_file.write(notice_file.read())
+            output_file.write(b"\n")
     output_file.close()
 
 def combine_notice_files_xml(files_with_same_hash, input_dir, output_filename):
@@ -154,26 +159,24 @@
     # Set up a filename to row id table (anchors inside tables don't work in
     # most browsers, but href's to table row ids do)
     id_table = {}
-    for file_key in files_with_same_hash.keys():
-        for filename in files_with_same_hash[file_key]:
+    for file_key, files in files_with_same_hash.items():
+        for filename in files:
              id_table[filename] = file_key
 
     # Open the output file, and output the header pieces
     output_file = open(output_filename, "wb")
 
-    print >> output_file, '<?xml version="1.0" encoding="utf-8"?>'
-    print >> output_file, "<licenses>"
+    output_file.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
+    output_file.write(b"<licenses>\n")
 
     # Flatten the list of lists into a single list of filenames
-    sorted_filenames = sorted(id_table.keys())
+    sorted_filenames = sorted(list(id_table))
 
     # Print out a nice table of contents
     for filename in sorted_filenames:
         stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
-        print >> output_file, '<file-name contentId="%s">%s</file-name>' % (id_table.get(filename), stripped_filename)
-
-    print >> output_file
-    print >> output_file
+        output_file.write(('<file-name contentId="%s">%s</file-name>\n' % (id_table.get(filename), stripped_filename)).encode())
+    output_file.write(b"\n\n")
 
     processed_file_keys = []
     # Output the individual notice file lists
@@ -183,11 +186,13 @@
             continue
         processed_file_keys.append(file_key)
 
-        print >> output_file, '<file-content contentId="%s"><![CDATA[%s]]></file-content>' % (file_key, html_escape(open(filename).read()))
-        print >> output_file
+        output_file.write(('<file-content contentId="%s"><![CDATA[' % file_key).encode())
+        with open(filename, "rb") as notice_file:
+            output_file.write(html_escape(notice_file.read()))
+        output_file.write(b"]]></file-content>\n\n")
 
     # Finish off the file output
-    print >> output_file, "</licenses>"
+    output_file.write(b"</licenses>\n")
     output_file.close()
 
 def get_args():
@@ -253,7 +258,7 @@
                 file_md5sum = md5sum(filename)
                 files_with_same_hash[file_md5sum].append(filename)
 
-    filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(files_with_same_hash.keys())]
+    filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(list(files_with_same_hash))]
 
     combine_notice_files_text(filesets, input_dir, txt_output_file, file_title)
 
diff --git a/sdk/member_type.go b/sdk/member_type.go
index ee27c86..9aab61d 100644
--- a/sdk/member_type.go
+++ b/sdk/member_type.go
@@ -35,7 +35,7 @@
 
 	// the dependency tag used for items in this list that can be used to determine the memberType
 	// for a resolved dependency.
-	dependencyTag android.SdkMemberTypeDependencyTag
+	dependencyTag android.SdkMemberDependencyTag
 }
 
 func (p *sdkMemberTypeListProperty) propertyName() string {
diff --git a/sdk/update.go b/sdk/update.go
index 96a6e69..89a5c92 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -185,7 +185,7 @@
 	s.multilibUsages = multilibNone
 	ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
 		tag := ctx.OtherModuleDependencyTag(child)
-		if memberTag, ok := tag.(android.SdkMemberTypeDependencyTag); ok {
+		if memberTag, ok := tag.(android.SdkMemberDependencyTag); ok {
 			memberType := memberTag.SdkMemberType(child)
 
 			// If a nil SdkMemberType was returned then this module should not be added to the sdk.
@@ -765,6 +765,8 @@
 	name string
 }
 
+var _ android.BpPropertyTag = propertyTag{}
+
 // A BpPropertyTag to add to a property that contains references to other sdk members.
 //
 // This will cause the references to be rewritten to a versioned reference in the version
@@ -1563,10 +1565,6 @@
 	return archInfo
 }
 
-func (archInfo *archTypeSpecificInfo) optimizableProperties() interface{} {
-	return archInfo.Properties
-}
-
 // Get the link type of the variant
 //
 // If the variant is not differentiated by link type then it returns "",
@@ -1608,8 +1606,7 @@
 	addSdkMemberPropertiesToSet(ctx, archInfo.Properties, archTypePropertySet)
 
 	for _, linkInfo := range archInfo.linkInfos {
-		linkPropertySet := archTypePropertySet.AddPropertySet(linkInfo.linkType)
-		addSdkMemberPropertiesToSet(ctx, linkInfo.Properties, linkPropertySet)
+		linkInfo.addToPropertySet(ctx, archTypePropertySet)
 	}
 }
 
@@ -1640,6 +1637,11 @@
 	return linkInfo
 }
 
+func (l *linkTypeSpecificInfo) addToPropertySet(ctx *memberContext, propertySet android.BpPropertySet) {
+	linkPropertySet := propertySet.AddPropertySet(l.linkType)
+	addSdkMemberPropertiesToSet(ctx, l.Properties, linkPropertySet)
+}
+
 func (l *linkTypeSpecificInfo) String() string {
 	return fmt.Sprintf("LinkType{%s}", l.linkType)
 }
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index c6e98c7..db66ae2 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -26,6 +26,7 @@
 	"android/soong/android"
 	"android/soong/bazel"
 	"android/soong/cc"
+	"android/soong/snapshot"
 	"android/soong/tradefed"
 )
 
@@ -195,6 +196,9 @@
 	return proptools.String(s.properties.Sub_dir)
 }
 
+func (s *ShBinary) RelativeInstallPath() string {
+	return s.SubDir()
+}
 func (s *ShBinary) Installable() bool {
 	return s.properties.Installable == nil || proptools.Bool(s.properties.Installable)
 }
@@ -548,3 +552,5 @@
 }
 
 var Bool = proptools.Bool
+
+var _ snapshot.RelativeInstallPath = (*ShBinary)(nil)
diff --git a/snapshot/Android.bp b/snapshot/Android.bp
index f17ac53..3354993 100644
--- a/snapshot/Android.bp
+++ b/snapshot/Android.bp
@@ -11,12 +11,20 @@
         "soong",
         "soong-android",
     ],
+    // Source file name convention is to include _snapshot as a
+    // file suffix for files that are generating snapshots.
     srcs: [
+        "host_fake_snapshot.go",
+        "host_snapshot.go",
         "recovery_snapshot.go",
         "snapshot.go",
         "snapshot_base.go",
         "util.go",
         "vendor_snapshot.go",
     ],
+    testSrcs: [
+        "host_test.go",
+        "test.go",
+    ],
     pluginFor: ["soong_build"],
 }
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
new file mode 100644
index 0000000..6b4e12b
--- /dev/null
+++ b/snapshot/host_fake_snapshot.go
@@ -0,0 +1,149 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package snapshot
+
+import (
+	"encoding/json"
+	"path/filepath"
+
+	"android/soong/android"
+)
+
+// The host_snapshot module creates a snapshot of host tools to be used
+// in a minimal source tree.   In order to create the host_snapshot the
+// user must explicitly list the modules to be included.  The
+// host-fake-snapshot, defined in this file, is a utility to help determine
+// which host modules are being used in the minimal source tree.
+//
+// The host-fake-snapshot is designed to run in a full source tree and
+// will result in a snapshot that contains an empty file for each host
+// tool found in the tree.  The fake snapshot is only used to determine
+// the host modules that the minimal source tree depends on, hence the
+// snapshot uses an empty file for each module and saves on having to
+// actually build any tool to generate the snapshot.  The fake snapshot
+// is compatible with an actual host_snapshot and is installed into a
+// minimal source tree via the development/vendor_snapshot/update.py
+// script.
+//
+// After generating the fake snapshot and installing into the minimal
+// source tree, the dependent modules are determined via the
+// development/vendor_snapshot/update.py script (see script for more
+// information).  These modules are then used to define the actual
+// host_snapshot to be used.  This is a similar process to the other
+// snapshots (vendor, recovery,...)
+//
+// Example
+//
+// Full source tree:
+//   1/ Generate fake host snapshot
+//
+// Minimal source tree:
+//   2/ Install the fake host snapshot
+//   3/ List the host modules used from the snapshot
+//   4/ Remove fake host snapshot
+//
+// Full source tree:
+//   4/ Create host_snapshot with modules identified in step 3
+//
+// Minimal source tree:
+//   5/ Install host snapshot
+//   6/ Build
+//
+// The host-fake-snapshot is a singleton module, that will be built
+// if HOST_FAKE_SNAPSHOT_ENABLE=true.
+
+func init() {
+	registerHostSnapshotComponents(android.InitRegistrationContext)
+}
+
+func registerHostSnapshotComponents(ctx android.RegistrationContext) {
+	ctx.RegisterSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton)
+}
+
+type hostFakeSingleton struct {
+	snapshotDir string
+	zipFile     android.OptionalPath
+}
+
+func (c *hostFakeSingleton) init() {
+	c.snapshotDir = "host-fake-snapshot"
+
+}
+func HostToolsFakeAndroidSingleton() android.Singleton {
+	singleton := &hostFakeSingleton{}
+	singleton.init()
+	return singleton
+}
+
+func (c *hostFakeSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	if !ctx.DeviceConfig().HostFakeSnapshotEnabled() {
+		return
+	}
+	// Find all host binary modules add 'fake' versions to snapshot
+	var outputs android.Paths
+	seen := make(map[string]bool)
+	var jsonData []SnapshotJsonFlags
+	ctx.VisitAllModules(func(module android.Module) {
+		if module.Target().Os != ctx.Config().BuildOSTarget.Os {
+			return
+		}
+		if module.Target().Arch.ArchType != ctx.Config().BuildOSTarget.Arch.ArchType {
+			return
+		}
+
+		if android.IsModulePrebuilt(module) {
+			return
+		}
+
+		if !module.Enabled() || module.IsHideFromMake() {
+			return
+		}
+		apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+		if !apexInfo.IsForPlatform() {
+			return
+		}
+		path := hostBinToolPath(module)
+		if path.Valid() && path.String() != "" {
+			outFile := filepath.Join(c.snapshotDir, path.String())
+			if !seen[outFile] {
+				seen[outFile] = true
+				outputs = append(outputs, WriteStringToFileRule(ctx, "", outFile))
+				jsonData = append(jsonData, *hostBinJsonDesc(module))
+			}
+		}
+	})
+
+	marsh, err := json.Marshal(jsonData)
+	if err != nil {
+		ctx.Errorf("host fake snapshot json marshal failure: %#v", err)
+		return
+	}
+	outputs = append(outputs, WriteStringToFileRule(ctx, string(marsh), filepath.Join(c.snapshotDir, "host_snapshot.json")))
+	c.zipFile = zipSnapshot(ctx, c.snapshotDir, c.snapshotDir, outputs)
+
+}
+func (c *hostFakeSingleton) MakeVars(ctx android.MakeVarsContext) {
+	if !c.zipFile.Valid() {
+		return
+	}
+	ctx.Phony(
+		"host-fake-snapshot",
+		c.zipFile.Path())
+
+	ctx.DistForGoal(
+		"host-fake-snapshot",
+		c.zipFile.Path())
+
+}
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
new file mode 100644
index 0000000..2a25a00
--- /dev/null
+++ b/snapshot/host_snapshot.go
@@ -0,0 +1,221 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package snapshot
+
+import (
+	"encoding/json"
+	"fmt"
+	"path/filepath"
+	"sort"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+//
+// The host_snapshot module creates a snapshot of the modules defined in
+// the deps property.  The modules within the deps property (host tools)
+// are ones that return a valid path via HostToolPath() of the
+// HostToolProvider.  The created snapshot contains the binaries and any
+// transitive PackagingSpecs of the included host tools, along with a JSON
+// meta file.
+//
+// The snapshot is installed into a source tree via
+// development/vendor_snapshot/update.py, the included modules are
+// provided as preferred prebuilts.
+//
+// To determine which tools to include in the host snapshot see
+// host_fake_snapshot.go.
+
+func init() {
+	registerHostBuildComponents(android.InitRegistrationContext)
+}
+
+func registerHostBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("host_snapshot", hostSnapshotFactory)
+}
+
+// Relative installation path
+type RelativeInstallPath interface {
+	RelativeInstallPath() string
+}
+
+type hostSnapshot struct {
+	android.ModuleBase
+	android.PackagingBase
+
+	zipFile    android.OptionalPath
+	installDir android.InstallPath
+}
+
+func hostSnapshotFactory() android.Module {
+	module := &hostSnapshot{}
+	initHostToolsModule(module)
+	return module
+}
+func initHostToolsModule(module *hostSnapshot) {
+	android.InitPackageModule(module)
+	android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon)
+}
+
+var dependencyTag = struct {
+	blueprint.BaseDependencyTag
+	android.InstallAlwaysNeededDependencyTag
+	android.PackagingItemAlwaysDepTag
+}{}
+
+func (f *hostSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
+	f.AddDeps(ctx, dependencyTag)
+}
+func (f *hostSnapshot) installFileName() string {
+	return f.Name() + ".zip"
+}
+
+// Create zipfile with JSON description, notice files... for dependent modules
+func (f *hostSnapshot) CreateMetaData(ctx android.ModuleContext, fileName string) android.OutputPath {
+	var jsonData []SnapshotJsonFlags
+	var metaPaths android.Paths
+
+	metaZipFile := android.PathForModuleOut(ctx, fileName).OutputPath
+
+	// Create JSON file based on the direct dependencies
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		desc := hostBinJsonDesc(dep)
+		if desc != nil {
+			jsonData = append(jsonData, *desc)
+		}
+		if len(dep.EffectiveLicenseFiles()) > 0 {
+			noticeFile := android.PathForModuleOut(ctx, "NOTICE_FILES", dep.Name()+".txt").OutputPath
+			android.CatFileRule(ctx, dep.EffectiveLicenseFiles(), noticeFile)
+			metaPaths = append(metaPaths, noticeFile)
+		}
+
+	})
+	// Sort notice paths and json data for repeatble build
+	sort.Slice(jsonData, func(i, j int) bool {
+		return (jsonData[i].ModuleName < jsonData[j].ModuleName)
+	})
+	sort.Slice(metaPaths, func(i, j int) bool {
+		return (metaPaths[i].String() < metaPaths[j].String())
+	})
+
+	marsh, err := json.Marshal(jsonData)
+	if err != nil {
+		ctx.ModuleErrorf("host snapshot json marshal failure: %#v", err)
+		return android.OutputPath{}
+	}
+
+	jsonZipFile := android.PathForModuleOut(ctx, "host_snapshot.json").OutputPath
+	metaPaths = append(metaPaths, jsonZipFile)
+	rspFile := android.PathForModuleOut(ctx, "host_snapshot.rsp").OutputPath
+	android.WriteFileRule(ctx, jsonZipFile, string(marsh))
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+
+	builder.Command().
+		BuiltTool("soong_zip").
+		FlagWithArg("-C ", android.PathForModuleOut(ctx).OutputPath.String()).
+		FlagWithOutput("-o ", metaZipFile).
+		FlagWithRspFileInputList("-r ", rspFile, metaPaths)
+	builder.Build("zip_meta", fmt.Sprintf("zipping meta data for %s", ctx.ModuleName()))
+
+	return metaZipFile
+}
+
+// Create the host tool zip file
+func (f *hostSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Create a zip file for the binaries, and a zip of the meta data, then merge zips
+	depsZipFile := android.PathForModuleOut(ctx, f.Name()+"_deps.zip").OutputPath
+	modsZipFile := android.PathForModuleOut(ctx, f.Name()+"_mods.zip").OutputPath
+	outputFile := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
+
+	f.installDir = android.PathForModuleInstall(ctx)
+
+	f.CopyDepsToZip(ctx, depsZipFile)
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().
+		BuiltTool("zip2zip").
+		FlagWithInput("-i ", depsZipFile).
+		FlagWithOutput("-o ", modsZipFile).
+		Text("**/*:" + proptools.ShellEscape(f.installDir.String()))
+
+	metaZipFile := f.CreateMetaData(ctx, f.Name()+"_meta.zip")
+
+	builder.Command().
+		BuiltTool("merge_zips").
+		Output(outputFile).
+		Input(metaZipFile).
+		Input(modsZipFile)
+
+	builder.Build("manifest", fmt.Sprintf("Adding manifest %s", f.installFileName()))
+	zip := ctx.InstallFile(f.installDir, f.installFileName(), outputFile)
+	f.zipFile = android.OptionalPathForPath(zip)
+
+}
+
+// Implements android.AndroidMkEntriesProvider
+func (f *hostSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
+	if !f.zipFile.Valid() {
+		return []android.AndroidMkEntries{}
+	}
+
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: f.zipFile,
+		DistFiles:  android.MakeDefaultDistFiles(f.zipFile.Path()),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
+			},
+		},
+	}}
+}
+
+// Get host tools path and relative install string helpers
+func hostBinToolPath(m android.Module) android.OptionalPath {
+	if provider, ok := m.(android.HostToolProvider); ok {
+		return provider.HostToolPath()
+	}
+	return android.OptionalPath{}
+
+}
+func hostRelativePathString(m android.Module) string {
+	var outString string
+	if rel, ok := m.(RelativeInstallPath); ok {
+		outString = rel.RelativeInstallPath()
+	}
+	return outString
+}
+
+// Create JSON description for given module, only create descriptions for binary modueles which
+// provide a valid HostToolPath
+func hostBinJsonDesc(m android.Module) *SnapshotJsonFlags {
+	path := hostBinToolPath(m)
+	relPath := hostRelativePathString(m)
+	if path.Valid() && path.String() != "" {
+		return &SnapshotJsonFlags{
+			ModuleName:          m.Name(),
+			ModuleStemName:      filepath.Base(path.String()),
+			Filename:            path.String(),
+			Required:            append(m.HostRequiredModuleNames(), m.RequiredModuleNames()...),
+			RelativeInstallPath: relPath,
+		}
+	}
+	return nil
+}
diff --git a/snapshot/host_test.go b/snapshot/host_test.go
new file mode 100644
index 0000000..ab9fedd
--- /dev/null
+++ b/snapshot/host_test.go
@@ -0,0 +1,170 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package snapshot
+
+import (
+	"path/filepath"
+	"testing"
+
+	"android/soong/android"
+)
+
+// host_snapshot and host-fake-snapshot test functions
+
+type hostTestModule struct {
+	android.ModuleBase
+	props struct {
+		Deps []string
+	}
+}
+
+func hostTestBinOut(bin string) string {
+	return filepath.Join("out", "bin", bin)
+}
+
+func (c *hostTestModule) HostToolPath() android.OptionalPath {
+	return (android.OptionalPathForPath(android.PathForTesting(hostTestBinOut(c.Name()))))
+}
+
+func hostTestModuleFactory() android.Module {
+	m := &hostTestModule{}
+	m.AddProperties(&m.props)
+	android.InitAndroidArchModule(m, android.HostSupported, android.MultilibFirst)
+	return m
+}
+func (m *hostTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	builtFile := android.PathForModuleOut(ctx, m.Name())
+	dir := ctx.Target().Arch.ArchType.Multilib
+	installDir := android.PathForModuleInstall(ctx, dir)
+	ctx.InstallFile(installDir, m.Name(), builtFile)
+}
+
+// Common blueprint used for testing
+var hostTestBp = `
+		license_kind {
+			name: "test_notice",
+			conditions: ["notice"],
+		}
+		license {
+			name: "host_test_license",
+			visibility: ["//visibility:public"],
+			license_kinds: [
+				"test_notice"
+			],
+			license_text: [
+				"NOTICE",
+			],
+		}
+		component {
+			name: "foo",
+			deps: ["bar"],
+		}
+		component {
+			name: "bar",
+			licenses: ["host_test_license"],
+		}
+		`
+
+var hostTestModBp = `
+		host_snapshot {
+			name: "test-host-snapshot",
+			deps: [
+				"foo",
+			],
+		}
+		`
+
+var prepareForHostTest = android.GroupFixturePreparers(
+	android.PrepareForTestWithAndroidBuildComponents,
+	android.PrepareForTestWithLicenses,
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("component", hostTestModuleFactory)
+	}),
+)
+
+// Prepare for host_snapshot test
+var prepareForHostModTest = android.GroupFixturePreparers(
+	prepareForHostTest,
+	android.FixtureWithRootAndroidBp(hostTestBp+hostTestModBp),
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		registerHostBuildComponents(ctx)
+	}),
+)
+
+// Prepare for fake host snapshot test disabled
+var prepareForFakeHostTest = android.GroupFixturePreparers(
+	prepareForHostTest,
+	android.FixtureWithRootAndroidBp(hostTestBp),
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		registerHostSnapshotComponents(ctx)
+	}),
+)
+
+// Prepare for fake host snapshot test enabled
+var prepareForFakeHostTestEnabled = android.GroupFixturePreparers(
+	prepareForFakeHostTest,
+	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.HostFakeSnapshotEnabled = true
+	}),
+)
+
+// Validate that a hostSnapshot object is created containing zip files and JSON file
+// content of zip file is not validated as this is done by PackagingSpecs
+func TestHostSnapshot(t *testing.T) {
+	result := prepareForHostModTest.RunTest(t)
+	t.Helper()
+	ctx := result.TestContext.ModuleForTests("test-host-snapshot", result.Config.BuildOS.String()+"_common")
+	mod := ctx.Module().(*hostSnapshot)
+	if ctx.MaybeOutput("host_snapshot.json").Rule == nil {
+		t.Error("Manifest file not found")
+	}
+	zips := []string{"_deps.zip", "_mods.zip", ".zip"}
+
+	for _, zip := range zips {
+		zFile := mod.Name() + zip
+		if ctx.MaybeOutput(zFile).Rule == nil {
+			t.Error("Zip file ", zFile, "not found")
+		}
+
+	}
+}
+
+// Validate fake host snapshot contains binary modules as well as the JSON meta file
+func TestFakeHostSnapshotEnable(t *testing.T) {
+	result := prepareForFakeHostTestEnabled.RunTest(t)
+	t.Helper()
+	bins := []string{"foo", "bar"}
+	ctx := result.TestContext.SingletonForTests("host-fake-snapshot")
+	if ctx.MaybeOutput(filepath.Join("host-fake-snapshot", "host_snapshot.json")).Rule == nil {
+		t.Error("Manifest file not found")
+	}
+	for _, bin := range bins {
+		if ctx.MaybeOutput(filepath.Join("host-fake-snapshot", hostTestBinOut(bin))).Rule == nil {
+			t.Error("Binary file ", bin, "not found")
+		}
+
+	}
+}
+
+// Validate not fake host snapshot if HostFakeSnapshotEnabled has not been set to true
+func TestFakeHostSnapshotDisable(t *testing.T) {
+	result := prepareForFakeHostTest.RunTest(t)
+	t.Helper()
+	ctx := result.TestContext.SingletonForTests("host-fake-snapshot")
+	if len(ctx.AllOutputs()) != 0 {
+		t.Error("Fake host snapshot not empty when disabled")
+	}
+
+}
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
index de93f3e..79d3cf6 100644
--- a/snapshot/snapshot_base.go
+++ b/snapshot/snapshot_base.go
@@ -102,3 +102,19 @@
 		return isDirectoryExcluded(filepath.Dir(dir), excludedMap, includedMap)
 	}
 }
+
+// This is to be saved as .json files, which is for development/vendor_snapshot/update.py.
+// These flags become Android.bp snapshot module properties.
+//
+// Attributes are optional and will be populated based on each module's need.
+// Common attributes are defined here, languages may extend this struct to add
+// additional attributes.
+type SnapshotJsonFlags struct {
+	ModuleName          string `json:",omitempty"`
+	RelativeInstallPath string `json:",omitempty"`
+	Filename            string `json:",omitempty"`
+	ModuleStemName      string `json:",omitempty"`
+
+	// dependencies
+	Required []string `json:",omitempty"`
+}
diff --git a/snapshot/test.go b/snapshot/test.go
new file mode 100644
index 0000000..346af2b
--- /dev/null
+++ b/snapshot/test.go
@@ -0,0 +1,24 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package snapshot
+
+import (
+	"os"
+	"testing"
+)
+
+func TestMain(m *testing.M) {
+	os.Exit(m.Run())
+}
diff --git a/snapshot/util.go b/snapshot/util.go
index 2297dfc..f447052 100644
--- a/snapshot/util.go
+++ b/snapshot/util.go
@@ -34,3 +34,22 @@
 	})
 	return outPath
 }
+
+// zip snapshot
+func zipSnapshot(ctx android.SingletonContext, dir string, baseName string, snapshotOutputs android.Paths) android.OptionalPath {
+	zipPath := android.PathForOutput(
+		ctx, dir, baseName+".zip")
+
+	zipRule := android.NewRuleBuilder(pctx, ctx)
+	rspFile := android.PathForOutput(
+		ctx, dir, baseName+"_list.rsp")
+
+	zipRule.Command().
+		BuiltTool("soong_zip").
+		FlagWithOutput("-o ", zipPath).
+		FlagWithArg("-C ", android.PathForOutput(ctx, dir).String()).
+		FlagWithRspFileInputList("-r ", rspFile, snapshotOutputs)
+
+	zipRule.Build(zipPath.String(), baseName+" snapshot "+zipPath.String())
+	return android.OptionalPathForPath(zipPath)
+}
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 65b91bc..a3a1aaf 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -250,7 +250,10 @@
 	newFile = filepath.Join(basePath, newFile)
 	oldFile := newFile + ".previous"
 
-	if _, err := os.Stat(newFile); err != nil {
+	if _, err := os.Stat(newFile); os.IsNotExist(err) {
+		// If the file doesn't exist, assume no installed files exist either
+		return
+	} else if err != nil {
 		ctx.Fatalf("Expected %q to be readable", newFile)
 	}