Merge "Apply sdk version check to product apps"
diff --git a/android/androidmk.go b/android/androidmk.go
index a8153cc..b5f4b2b 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -29,7 +29,11 @@
 )
 
 func init() {
-	RegisterSingletonType("androidmk", AndroidMkSingleton)
+	RegisterAndroidMkBuildComponents(InitRegistrationContext)
+}
+
+func RegisterAndroidMkBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
 }
 
 // Deprecated: consider using AndroidMkEntriesProvider instead, especially if you're not going to
diff --git a/android/apex.go b/android/apex.go
index 43a42df..a4de6dd 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -15,7 +15,9 @@
 package android
 
 import (
+	"fmt"
 	"sort"
+	"strconv"
 	"sync"
 )
 
@@ -25,6 +27,8 @@
 
 	// Whether this apex variant needs to target Android 10
 	LegacyAndroid10Support bool
+
+	MinSdkVersion int
 }
 
 // ApexModule is the interface that a module type is expected to implement if
@@ -86,6 +90,13 @@
 	// DepIsInSameApex tests if the other module 'dep' is installed to the same
 	// APEX as this module
 	DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
+
+	// Returns the highest version which is <= min_sdk_version.
+	// For example, with min_sdk_version is 10 and versionList is [9,11]
+	// it returns 9.
+	ChooseSdkVersion(versionList []string, useLatest bool) (string, error)
+
+	ShouldSupportAndroid10() bool
 }
 
 type ApexProperties struct {
@@ -116,6 +127,10 @@
 	return m
 }
 
+func (m *ApexModuleBase) ApexAvailable() []string {
+	return m.ApexProperties.Apex_available
+}
+
 func (m *ApexModuleBase) BuildForApexes(apexes []ApexInfo) {
 	m.apexVariationsLock.Lock()
 	defer m.apexVariationsLock.Unlock()
@@ -177,6 +192,24 @@
 	return true
 }
 
+func (m *ApexModuleBase) ChooseSdkVersion(versionList []string, useLatest bool) (string, error) {
+	if useLatest {
+		return versionList[len(versionList)-1], nil
+	}
+	minSdkVersion := m.ApexProperties.Info.MinSdkVersion
+	for i := range versionList {
+		ver, _ := strconv.Atoi(versionList[len(versionList)-i-1])
+		if ver <= minSdkVersion {
+			return versionList[len(versionList)-i-1], nil
+		}
+	}
+	return "", fmt.Errorf("min_sdk_version is set %v, but not found in %v", minSdkVersion, versionList)
+}
+
+func (m *ApexModuleBase) ShouldSupportAndroid10() bool {
+	return !m.IsForPlatform() && (m.ApexProperties.Info.MinSdkVersion <= 29 || m.ApexProperties.Info.LegacyAndroid10Support)
+}
+
 func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
 	for _, n := range m.ApexProperties.Apex_available {
 		if n == AvailableToPlatform || n == availableToAnyApex {
diff --git a/android/arch.go b/android/arch.go
index 73a490d..e440486 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -596,7 +596,7 @@
 }()
 
 var (
-	osTypeList      []OsType
+	OsTypeList      []OsType
 	commonTargetMap = make(map[string]Target)
 
 	NoOsType    OsType
@@ -607,6 +607,10 @@
 	Android     = NewOsType("android", Device, false)
 	Fuchsia     = NewOsType("fuchsia", Device, false)
 
+	// A pseudo OSType for a common os variant, which is OSType agnostic and which
+	// has dependencies on all the OS variants.
+	CommonOS = NewOsType("common_os", Generic, false)
+
 	osArchTypeMap = map[OsType][]ArchType{
 		Linux:       []ArchType{X86, X86_64},
 		LinuxBionic: []ArchType{X86_64},
@@ -668,7 +672,7 @@
 
 		DefaultDisabled: defDisabled,
 	}
-	osTypeList = append(osTypeList, os)
+	OsTypeList = append(OsTypeList, os)
 
 	if _, found := commonTargetMap[name]; found {
 		panic(fmt.Errorf("Found Os type duplicate during OsType registration: %q", name))
@@ -680,7 +684,7 @@
 }
 
 func osByName(name string) OsType {
-	for _, os := range osTypeList {
+	for _, os := range OsTypeList {
 		if os.Name == name {
 			return os
 		}
@@ -746,7 +750,7 @@
 
 	var moduleOSList []OsType
 
-	for _, os := range osTypeList {
+	for _, os := range OsTypeList {
 		supportedClass := false
 		for _, osClass := range osClasses {
 			if os.Class == osClass {
@@ -775,12 +779,64 @@
 		osNames[i] = os.String()
 	}
 
-	modules := mctx.CreateVariations(osNames...)
-	for i, m := range modules {
-		m.(Module).base().commonProperties.CompileOS = moduleOSList[i]
-		m.(Module).base().setOSProperties(mctx)
+	createCommonOSVariant := base.commonProperties.CreateCommonOSVariant
+	if createCommonOSVariant {
+		// A CommonOS variant was requested so add it to the list of OS's variants to
+		// create. It needs to be added to the end because it needs to depend on the
+		// the other variants in the list returned by CreateVariations(...) and inter
+		// variant dependencies can only be created from a later variant in that list to
+		// an earlier one. That is because variants are always processed in the order in
+		// which they are returned from CreateVariations(...).
+		osNames = append(osNames, CommonOS.Name)
+		moduleOSList = append(moduleOSList, CommonOS)
 	}
 
+	modules := mctx.CreateVariations(osNames...)
+	for i, m := range modules {
+		m.base().commonProperties.CompileOS = moduleOSList[i]
+		m.base().setOSProperties(mctx)
+	}
+
+	if createCommonOSVariant {
+		// A CommonOS variant was requested so add dependencies from it (the last one in
+		// the list) to the OS type specific variants.
+		last := len(modules) - 1
+		commonOSVariant := modules[last]
+		commonOSVariant.base().commonProperties.CommonOSVariant = true
+		for _, module := range modules[0:last] {
+			// Ignore modules that are enabled. Note, this will only avoid adding
+			// dependencies on OsType variants that are explicitly disabled in their
+			// properties. The CommonOS variant will still depend on disabled variants
+			// if they are disabled afterwards, e.g. in archMutator if
+			if module.Enabled() {
+				mctx.AddInterVariantDependency(commonOsToOsSpecificVariantTag, commonOSVariant, module)
+			}
+		}
+	}
+}
+
+// Identifies the dependency from CommonOS variant to the os specific variants.
+type commonOSTag struct{ blueprint.BaseDependencyTag }
+
+var commonOsToOsSpecificVariantTag = commonOSTag{}
+
+// Get the OsType specific variants for the current CommonOS variant.
+//
+// The returned list will only contain enabled OsType specific variants of the
+// module referenced in the supplied context. An empty list is returned if there
+// are no enabled variants or the supplied context is not for an CommonOS
+// variant.
+func GetOsSpecificVariantsOfCommonOSVariant(mctx BaseModuleContext) []Module {
+	var variants []Module
+	mctx.VisitDirectDeps(func(m Module) {
+		if mctx.OtherModuleDependencyTag(m) == commonOsToOsSpecificVariantTag {
+			if m.Enabled() {
+				variants = append(variants, m)
+			}
+		}
+	})
+
+	return variants
 }
 
 // archMutator splits a module into a variant for each Target requested by the module.  Target selection
@@ -821,6 +877,15 @@
 	}
 
 	os := base.commonProperties.CompileOS
+	if os == CommonOS {
+		// Make sure that the target related properties are initialized for the
+		// CommonOS variant.
+		addTargetProperties(module, commonTargetMap[os.Name], nil, true)
+
+		// Do not create arch specific variants for the CommonOS variant.
+		return
+	}
+
 	osTargets := mctx.Config().Targets[os]
 	image := base.commonProperties.ImageVariation
 	// Filter NativeBridge targets unless they are explicitly supported
@@ -881,15 +946,17 @@
 
 	modules := mctx.CreateVariations(targetNames...)
 	for i, m := range modules {
-		m.(Module).base().commonProperties.CompileTarget = targets[i]
-		m.(Module).base().commonProperties.CompileMultiTargets = multiTargets
-		if i == 0 {
-			m.(Module).base().commonProperties.CompilePrimary = true
-		}
+		addTargetProperties(m, targets[i], multiTargets, i == 0)
 		m.(Module).base().setArchProperties(mctx)
 	}
 }
 
+func addTargetProperties(m Module, target Target, multiTargets []Target, primaryTarget bool) {
+	m.base().commonProperties.CompileTarget = target
+	m.base().commonProperties.CompileMultiTargets = multiTargets
+	m.base().commonProperties.CompilePrimary = primaryTarget
+}
+
 func decodeMultilib(base *ModuleBase, class OsClass) (multilib, extraMultilib string) {
 	switch class {
 	case Device:
@@ -1004,7 +1071,7 @@
 			"Arm_on_x86",
 			"Arm_on_x86_64",
 		}
-		for _, os := range osTypeList {
+		for _, os := range OsTypeList {
 			targets = append(targets, os.Field)
 
 			for _, archType := range osArchTypeMap[os] {
@@ -1711,6 +1778,8 @@
 	return ret
 }
 
+// Return the set of Os specific common architecture targets for each Os in a list of
+// targets.
 func getCommonTargets(targets []Target) []Target {
 	var ret []Target
 	set := make(map[string]bool)
diff --git a/android/config.go b/android/config.go
index b2bda2a..9b1297c 100644
--- a/android/config.go
+++ b/android/config.go
@@ -359,6 +359,9 @@
 		return Config{}, err
 	}
 
+	// Make the CommonOS OsType available for all products.
+	targets[CommonOS] = []Target{commonTargetMap[CommonOS.Name]}
+
 	var archConfig []archConfig
 	if Bool(config.Mega_device) {
 		archConfig = getMegaDeviceConfig()
diff --git a/android/module.go b/android/module.go
index 1026bdf..2e33056 100644
--- a/android/module.go
+++ b/android/module.go
@@ -132,6 +132,9 @@
 
 	Target() Target
 	TargetPrimary() bool
+
+	// The additional arch specific targets (e.g. 32/64 bit) that this module variant is
+	// responsible for creating.
 	MultiTargets() []Target
 	Arch() Arch
 	Os() OsType
@@ -364,6 +367,10 @@
 		}
 	}
 
+	// If set to true then the archMutator will create variants for each arch specific target
+	// (e.g. 32/64) that the module is required to produce. If set to false then it will only
+	// create a variant for the architecture and will list the additional arch specific targets
+	// that the variant needs to produce in the CompileMultiTargets property.
 	UseTargetVariants bool   `blueprint:"mutated"`
 	Default_multilib  string `blueprint:"mutated"`
 
@@ -442,16 +449,56 @@
 		Suffix *string `android:"arch_variant"`
 	} `android:"arch_variant"`
 
-	// Set by TargetMutator
-	CompileOS           OsType   `blueprint:"mutated"`
-	CompileTarget       Target   `blueprint:"mutated"`
+	// The OsType of artifacts that this module variant is responsible for creating.
+	//
+	// Set by osMutator
+	CompileOS OsType `blueprint:"mutated"`
+
+	// The Target of artifacts that this module variant is responsible for creating.
+	//
+	// Set by archMutator
+	CompileTarget Target `blueprint:"mutated"`
+
+	// The additional arch specific targets (e.g. 32/64 bit) that this module variant is
+	// responsible for creating.
+	//
+	// By default this is nil as, where necessary, separate variants are created for the
+	// different multilib types supported and that information is encapsulated in the
+	// CompileTarget so the module variant simply needs to create artifacts for that.
+	//
+	// However, if UseTargetVariants is set to false (e.g. by
+	// InitAndroidMultiTargetsArchModule)  then no separate variants are created for the
+	// multilib targets. Instead a single variant is created for the architecture and
+	// this contains the multilib specific targets that this variant should create.
+	//
+	// Set by archMutator
 	CompileMultiTargets []Target `blueprint:"mutated"`
-	CompilePrimary      bool     `blueprint:"mutated"`
+
+	// True if the module variant's CompileTarget is the primary target
+	//
+	// Set by archMutator
+	CompilePrimary bool `blueprint:"mutated"`
 
 	// Set by InitAndroidModule
 	HostOrDeviceSupported HostOrDeviceSupported `blueprint:"mutated"`
 	ArchSpecific          bool                  `blueprint:"mutated"`
 
+	// If set to true then a CommonOS variant will be created which will have dependencies
+	// on all its OsType specific variants. Used by sdk/module_exports to create a snapshot
+	// that covers all os and architecture variants.
+	//
+	// The OsType specific variants can be retrieved by calling
+	// GetOsSpecificVariantsOfCommonOSVariant
+	//
+	// Set at module initialization time by calling InitCommonOSAndroidMultiTargetsArchModule
+	CreateCommonOSVariant bool `blueprint:"mutated"`
+
+	// If set to true then this variant is the CommonOS variant that has dependencies on its
+	// OsType specific variants.
+	//
+	// Set by osMutator.
+	CommonOSVariant bool `blueprint:"mutated"`
+
 	SkipInstall bool `blueprint:"mutated"`
 
 	NamespaceExportedToMake bool `blueprint:"mutated"`
@@ -584,6 +631,14 @@
 	m.base().commonProperties.UseTargetVariants = false
 }
 
+// As InitAndroidMultiTargetsArchModule except it creates an additional CommonOS variant that
+// has dependencies on all the OsType specific variants.
+func InitCommonOSAndroidMultiTargetsArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
+	InitAndroidArchModule(m, hod, defaultMultilib)
+	m.base().commonProperties.UseTargetVariants = false
+	m.base().commonProperties.CreateCommonOSVariant = true
+}
+
 // A ModuleBase object contains the properties that are common to all Android
 // modules.  It should be included as an anonymous field in every module
 // struct definition.  InitAndroidModule should then be called from the module's
@@ -775,6 +830,11 @@
 	return m.commonProperties.ArchSpecific
 }
 
+// True if the current variant is a CommonOS variant, false otherwise.
+func (m *ModuleBase) IsCommonOSVariant() bool {
+	return m.commonProperties.CommonOSVariant
+}
+
 func (m *ModuleBase) OsClassSupported() []OsClass {
 	switch m.commonProperties.HostOrDeviceSupported {
 	case HostSupported:
@@ -1103,8 +1163,11 @@
 	blueprintCtx.GetMissingDependencies()
 
 	// For the final GenerateAndroidBuildActions pass, require that all visited dependencies Soong modules and
-	// are enabled.
-	ctx.baseModuleContext.strictVisitDeps = true
+	// are enabled. Unless the module is a CommonOS variant which may have dependencies on disabled variants
+	// (because the dependencies are added before the modules are disabled). The
+	// GetOsSpecificVariantsOfCommonOSVariant(...) method will ensure that the disabled variants are
+	// ignored.
+	ctx.baseModuleContext.strictVisitDeps = !m.IsCommonOSVariant()
 
 	if ctx.config.captureBuild {
 		ctx.ruleParams = make(map[blueprint.Rule]blueprint.RuleParams)
@@ -1297,16 +1360,20 @@
 	strictVisitDeps bool // If true, enforce that all dependencies are enabled
 }
 
-func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string { return b.bp.OtherModuleName(m) }
-func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string  { return b.bp.OtherModuleDir(m) }
+func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string {
+	return b.bp.OtherModuleName(m)
+}
+func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string { return b.bp.OtherModuleDir(m) }
 func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) {
 	b.bp.OtherModuleErrorf(m, fmt, args...)
 }
 func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag {
 	return b.bp.OtherModuleDependencyTag(m)
 }
-func (b *baseModuleContext) OtherModuleExists(name string) bool        { return b.bp.OtherModuleExists(name) }
-func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string { return b.bp.OtherModuleType(m) }
+func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) }
+func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string {
+	return b.bp.OtherModuleType(m)
+}
 
 func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module {
 	return b.bp.GetDirectDepWithTag(name, tag)
diff --git a/android/neverallow.go b/android/neverallow.go
index 0cb2029..8fcfb8a 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -102,6 +102,7 @@
 			In("vendor", "device").
 			With("vndk.enabled", "true").
 			Without("vendor", "true").
+			Without("product_specific", "true").
 			Because("the VNDK can never contain a library that is device dependent."),
 		NeverAllow().
 			With("vndk.enabled", "true").
diff --git a/android/notices.go b/android/notices.go
index bf273b5..07cf3e4 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -22,7 +22,7 @@
 
 func init() {
 	pctx.SourcePathVariable("merge_notices", "build/soong/scripts/mergenotice.py")
-	pctx.SourcePathVariable("generate_notice", "build/make/tools/generate-notice-files.py")
+	pctx.SourcePathVariable("generate_notice", "build/soong/scripts/generate-notice-files.py")
 
 	pctx.HostBinToolVariable("minigzip", "minigzip")
 }
diff --git a/android/paths.go b/android/paths.go
index 8b373da..8bb9a96 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -470,6 +470,14 @@
 // FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each.  It
 // modifies the Paths slice contents in place, and returns a subslice of the original slice.
 func FirstUniquePaths(list Paths) Paths {
+	// 128 was chosen based on BenchmarkFirstUniquePaths results.
+	if len(list) > 128 {
+		return firstUniquePathsMap(list)
+	}
+	return firstUniquePathsList(list)
+}
+
+func firstUniquePathsList(list Paths) Paths {
 	k := 0
 outer:
 	for i := 0; i < len(list); i++ {
@@ -484,6 +492,20 @@
 	return list[:k]
 }
 
+func firstUniquePathsMap(list Paths) Paths {
+	k := 0
+	seen := make(map[Path]bool, len(list))
+	for i := 0; i < len(list); i++ {
+		if seen[list[i]] {
+			continue
+		}
+		seen[list[i]] = true
+		list[k] = list[i]
+		k++
+	}
+	return list[:k]
+}
+
 // LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each.  It
 // modifies the Paths slice contents in place, and returns a subslice of the original slice.
 func LastUniquePaths(list Paths) Paths {
@@ -1254,8 +1276,8 @@
 	return ret
 }
 
-func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath {
-	paths = append([]string{"ndk"}, paths...)
+func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath {
+	paths = append([]string{prefix}, paths...)
 	path, err := validatePath(paths...)
 	if err != nil {
 		reportPathError(ctx, err)
@@ -1263,6 +1285,14 @@
 	return InstallPath{basePath{path, ctx.Config(), ""}, ""}
 }
 
+func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath {
+	return pathForNdkOrSdkInstall(ctx, "ndk", paths)
+}
+
+func PathForMainlineSdksInstall(ctx PathContext, paths ...string) InstallPath {
+	return pathForNdkOrSdkInstall(ctx, "mainline-sdks", paths)
+}
+
 func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
 	rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String())
 
diff --git a/android/paths_test.go b/android/paths_test.go
index f1908ac..9b45d3f 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -18,6 +18,7 @@
 	"errors"
 	"fmt"
 	"reflect"
+	"strconv"
 	"strings"
 	"testing"
 
@@ -1255,3 +1256,51 @@
 	// out/system/framework/boot.art out/system/framework/oat/arm/boot.vdex
 	// boot.art oat/arm/boot.vdex
 }
+
+func BenchmarkFirstUniquePaths(b *testing.B) {
+	implementations := []struct {
+		name string
+		f    func(Paths) Paths
+	}{
+		{
+			name: "list",
+			f:    firstUniquePathsList,
+		},
+		{
+			name: "map",
+			f:    firstUniquePathsMap,
+		},
+	}
+	const maxSize = 1024
+	uniquePaths := make(Paths, maxSize)
+	for i := range uniquePaths {
+		uniquePaths[i] = PathForTesting(strconv.Itoa(i))
+	}
+	samePath := make(Paths, maxSize)
+	for i := range samePath {
+		samePath[i] = uniquePaths[0]
+	}
+
+	f := func(b *testing.B, imp func(Paths) Paths, paths Paths) {
+		for i := 0; i < b.N; i++ {
+			b.ReportAllocs()
+			paths = append(Paths(nil), paths...)
+			imp(paths)
+		}
+	}
+
+	for n := 1; n <= maxSize; n <<= 1 {
+		b.Run(strconv.Itoa(n), func(b *testing.B) {
+			for _, implementation := range implementations {
+				b.Run(implementation.name, func(b *testing.B) {
+					b.Run("same", func(b *testing.B) {
+						f(b, implementation.f, samePath[:n])
+					})
+					b.Run("unique", func(b *testing.B) {
+						f(b, implementation.f, uniquePaths[:n])
+					})
+				})
+			}
+		})
+	}
+}
diff --git a/android/sdk.go b/android/sdk.go
index d13ad7d..969e21a 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -302,7 +302,46 @@
 	//
 	// The SdkMember is guaranteed to contain variants for which the
 	// IsInstance(Module) method returned true.
+	//
+	// deprecated Use AddPrebuiltModule() instead.
 	BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember)
+
+	// Add a prebuilt module that the sdk will populate.
+	//
+	// Returning nil from this will cause the sdk module type to use the deprecated BuildSnapshot
+	// method to build the snapshot. That method is deprecated because it requires the SdkMemberType
+	// implementation to do all the word.
+	//
+	// Otherwise, returning a non-nil value from this will cause the sdk module type to do the
+	// majority of the work to generate the snapshot. The sdk module code generates the snapshot
+	// as follows:
+	//
+	// * A properties struct of type SdkMemberProperties is created for each variant and
+	//   populated with information from the variant by calling PopulateFromVariant(SdkAware)
+	//   on the struct.
+	//
+	// * An additional properties struct is created into which the common properties will be
+	//   added.
+	//
+	// * The variant property structs are analysed to find exported (capitalized) fields which
+	//   have common values. Those fields are cleared and the common value added to the common
+	//   properties.
+	//
+	// * The sdk module type populates the BpModule structure, creating the arch specific
+	//   structure and calls AddToPropertySet(...) on the properties struct to add the member
+	//   specific properties in the correct place in the structure.
+	//
+	// * Finally, the FinalizeModule(...) method is called to add any additional properties.
+	//   This was created to allow the property ordering in existing tests to be maintained so
+	//   as to avoid having to change tests while refactoring.
+	//
+	AddPrebuiltModule(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember) BpModule
+
+	// Add any additional properties to the end of the module.
+	FinalizeModule(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember, bpModule BpModule)
+
+	// Create a structure into which variant specific properties can be added.
+	CreateVariantPropertiesStruct() SdkMemberProperties
 }
 
 // Base type for SdkMemberType implementations.
@@ -324,6 +363,23 @@
 	return b.TransitiveSdkMembers
 }
 
+func (b *SdkMemberTypeBase) BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember) {
+	panic("override AddPrebuiltModule")
+}
+
+func (b *SdkMemberTypeBase) AddPrebuiltModule(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember) BpModule {
+	// Returning nil causes the legacy BuildSnapshot method to be used.
+	return nil
+}
+
+func (b *SdkMemberTypeBase) FinalizeModule(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember, module BpModule) {
+	// Do nothing by default
+}
+
+func (b *SdkMemberTypeBase) CreateVariantPropertiesStruct() SdkMemberProperties {
+	panic("override me")
+}
+
 // Encapsulates the information about registered SdkMemberTypes.
 type SdkMemberTypesRegistry struct {
 	// The list of types sorted by property name.
@@ -389,3 +445,50 @@
 		SdkMemberTypes = SdkMemberTypes.copyAndAppend(memberType)
 	}
 }
+
+// Base structure for all implementations of SdkMemberProperties.
+//
+// Contains common properties that apply across many different member types. These
+// are not affected by the optimization to extract common values.
+type SdkMemberPropertiesBase struct {
+	// The setting to use for the compile_multilib property.
+	Compile_multilib string
+
+	// The number of unique os types supported by the member variants.
+	Os_count int
+
+	// The os type for which these properties refer.
+	Os OsType
+}
+
+// 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.
+func (b *SdkMemberPropertiesBase) OsPrefix() string {
+	if b.Os_count == 1 {
+		return ""
+	} else {
+		return b.Os.Name
+	}
+}
+
+func (b *SdkMemberPropertiesBase) Base() *SdkMemberPropertiesBase {
+	return b
+}
+
+// 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.
+type SdkMemberProperties interface {
+	// Access the base structure.
+	Base() *SdkMemberPropertiesBase
+
+	// Populate the structure with information from the variant.
+	PopulateFromVariant(variant SdkAware)
+
+	// Add the information from the structure to the property set.
+	AddToPropertySet(sdkModuleContext ModuleContext, builder SnapshotBuilder, propertySet BpPropertySet)
+}
diff --git a/android/testing.go b/android/testing.go
index 9aff039..90989ef 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -410,6 +410,10 @@
 
 }
 
+func SetInMakeForTests(config Config) {
+	config.inMake = true
+}
+
 func AndroidMkEntriesForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) []AndroidMkEntries {
 	var p AndroidMkEntriesProvider
 	var ok bool
diff --git a/android/util.go b/android/util.go
index ade851e..e74b64e 100644
--- a/android/util.go
+++ b/android/util.go
@@ -193,6 +193,14 @@
 // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
 // each.  It modifies the slice contents in place, and returns a subslice of the original slice.
 func FirstUniqueStrings(list []string) []string {
+	// 128 was chosen based on BenchmarkFirstUniqueStrings results.
+	if len(list) > 128 {
+		return firstUniqueStringsMap(list)
+	}
+	return firstUniqueStringsList(list)
+}
+
+func firstUniqueStringsList(list []string) []string {
 	k := 0
 outer:
 	for i := 0; i < len(list); i++ {
@@ -207,6 +215,20 @@
 	return list[:k]
 }
 
+func firstUniqueStringsMap(list []string) []string {
+	k := 0
+	seen := make(map[string]bool, len(list))
+	for i := 0; i < len(list); i++ {
+		if seen[list[i]] {
+			continue
+		}
+		seen[list[i]] = true
+		list[k] = list[i]
+		k++
+	}
+	return list[:k]
+}
+
 // LastUniqueStrings returns all unique elements of a slice of strings, keeping the last copy of
 // each.  It modifies the slice contents in place, and returns a subslice of the original slice.
 func LastUniqueStrings(list []string) []string {
diff --git a/android/util_test.go b/android/util_test.go
index 1f9ca36..25b52ca 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"reflect"
+	"strconv"
 	"testing"
 )
 
@@ -59,15 +60,25 @@
 }
 
 func TestFirstUniqueStrings(t *testing.T) {
-	for _, testCase := range firstUniqueStringsTestCases {
-		out := FirstUniqueStrings(testCase.in)
-		if !reflect.DeepEqual(out, testCase.out) {
+	f := func(t *testing.T, imp func([]string) []string, in, want []string) {
+		t.Helper()
+		out := imp(in)
+		if !reflect.DeepEqual(out, want) {
 			t.Errorf("incorrect output:")
-			t.Errorf("     input: %#v", testCase.in)
-			t.Errorf("  expected: %#v", testCase.out)
+			t.Errorf("     input: %#v", in)
+			t.Errorf("  expected: %#v", want)
 			t.Errorf("       got: %#v", out)
 		}
 	}
+
+	for _, testCase := range firstUniqueStringsTestCases {
+		t.Run("list", func(t *testing.T) {
+			f(t, firstUniqueStringsList, testCase.in, testCase.out)
+		})
+		t.Run("map", func(t *testing.T) {
+			f(t, firstUniqueStringsMap, testCase.in, testCase.out)
+		})
+	}
 }
 
 var lastUniqueStringsTestCases = []struct {
@@ -568,3 +579,51 @@
 		})
 	}
 }
+
+func BenchmarkFirstUniqueStrings(b *testing.B) {
+	implementations := []struct {
+		name string
+		f    func([]string) []string
+	}{
+		{
+			name: "list",
+			f:    firstUniqueStringsList,
+		},
+		{
+			name: "map",
+			f:    firstUniqueStringsMap,
+		},
+	}
+	const maxSize = 1024
+	uniqueStrings := make([]string, maxSize)
+	for i := range uniqueStrings {
+		uniqueStrings[i] = strconv.Itoa(i)
+	}
+	sameString := make([]string, maxSize)
+	for i := range sameString {
+		sameString[i] = uniqueStrings[0]
+	}
+
+	f := func(b *testing.B, imp func([]string) []string, s []string) {
+		for i := 0; i < b.N; i++ {
+			b.ReportAllocs()
+			s = append([]string(nil), s...)
+			imp(s)
+		}
+	}
+
+	for n := 1; n <= maxSize; n <<= 1 {
+		b.Run(strconv.Itoa(n), func(b *testing.B) {
+			for _, implementation := range implementations {
+				b.Run(implementation.name, func(b *testing.B) {
+					b.Run("same", func(b *testing.B) {
+						f(b, implementation.f, sameString[:n])
+					})
+					b.Run("unique", func(b *testing.B) {
+						f(b, implementation.f, uniqueStrings[:n])
+					})
+				})
+			}
+		})
+	}
+}
diff --git a/apex/apex.go b/apex/apex.go
index bef4e42..5a5c02f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -19,6 +19,7 @@
 	"path"
 	"path/filepath"
 	"sort"
+	"strconv"
 	"strings"
 	"sync"
 
@@ -1028,7 +1029,15 @@
 	var apexBundles []android.ApexInfo
 	var directDep bool
 	if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
-		apexBundles = []android.ApexInfo{{mctx.ModuleName(), proptools.Bool(a.properties.Legacy_android10_support)}}
+		minSdkVersion := a.minSdkVersion(mctx)
+
+		apexBundles = []android.ApexInfo{
+			android.ApexInfo{
+				ApexName:               mctx.ModuleName(),
+				LegacyAndroid10Support: proptools.Bool(a.properties.Legacy_android10_support),
+				MinSdkVersion:          minSdkVersion,
+			},
+		}
 		directDep = true
 	} else if am, ok := mctx.Module().(android.ApexModule); ok {
 		apexBundles = am.ApexVariations()
@@ -1967,6 +1976,18 @@
 	})
 }
 
+func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) int {
+	ver := proptools.StringDefault(a.properties.Min_sdk_version, "current")
+	if ver != "current" {
+		minSdkVersion, err := strconv.Atoi(ver)
+		if err != nil {
+			ctx.PropertyErrorf("min_sdk_version", "should be \"current\" or <number>, but %q", ver)
+		}
+		return minSdkVersion
+	}
+	return android.FutureApiLevel
+}
+
 // Ensures that the dependencies are marked as available for this APEX
 func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
 	// Let's be practical. Availability for test, host, and the VNDK apex isn't important
@@ -1979,7 +2000,7 @@
 		if externalDep || to.AvailableFor(apexName) || whitelistedApexAvailable(apexName, to) {
 			return
 		}
-		ctx.ModuleErrorf("requires %q that is not available for the APEX.", to.Name())
+		ctx.ModuleErrorf("%q requires %q that is not available for the APEX.", from.Name(), to.Name())
 	})
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 6d9ad26..7b842da 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -984,6 +984,297 @@
 	ensureContains(t, libFlags, "libdl/android_arm64_armv8-a_shared/libdl.so")
 }
 
+func TestApexUseStubsAccordingToMinSdkVersionInUnbundledBuild(t *testing.T) {
+	// there are three links between liba --> libz
+	// 1) myapex -> libx -> liba -> libz    : this should be #2 link, but fallback to #1
+	// 2) otherapex -> liby -> liba -> libz : this should be #3 link
+	// 3) (platform) -> liba -> libz        : this should be non-stub link
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libx"],
+			min_sdk_version: "2",
+		}
+
+		apex {
+			name: "otherapex",
+			key: "myapex.key",
+			native_shared_libs: ["liby"],
+			min_sdk_version: "3",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libx",
+			shared_libs: ["liba"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "liby",
+			shared_libs: ["liba"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "otherapex" ],
+		}
+
+		cc_library {
+			name: "liba",
+			shared_libs: ["libz"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"//apex_available:anyapex",
+				"//apex_available:platform",
+			],
+		}
+
+		cc_library {
+			name: "libz",
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["1", "3"],
+			},
+		}
+	`, withUnbundledBuild)
+
+	expectLink := func(from, from_variant, to, to_variant string) {
+		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
+	}
+	expectNoLink := func(from, from_variant, to, to_variant string) {
+		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
+	}
+	// platform liba is linked to non-stub version
+	expectLink("liba", "shared", "libz", "shared")
+	// liba in myapex is linked to #1
+	expectLink("liba", "shared_myapex", "libz", "shared_1")
+	expectNoLink("liba", "shared_myapex", "libz", "shared_3")
+	expectNoLink("liba", "shared_myapex", "libz", "shared")
+	// liba in otherapex is linked to #3
+	expectLink("liba", "shared_otherapex", "libz", "shared_3")
+	expectNoLink("liba", "shared_otherapex", "libz", "shared_1")
+	expectNoLink("liba", "shared_otherapex", "libz", "shared")
+}
+
+func TestApexMinSdkVersionDefaultsToLatest(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libx"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libx",
+			shared_libs: ["libz"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "libz",
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["1", "2"],
+			},
+		}
+	`)
+
+	expectLink := func(from, from_variant, to, to_variant string) {
+		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
+	}
+	expectNoLink := func(from, from_variant, to, to_variant string) {
+		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
+	}
+	expectLink("libx", "shared_myapex", "libz", "shared_2")
+	expectNoLink("libx", "shared_myapex", "libz", "shared_1")
+	expectNoLink("libx", "shared_myapex", "libz", "shared")
+}
+
+func TestPlatformUsesLatestStubsFromApexes(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libx"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libx",
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+			stubs: {
+				versions: ["1", "2"],
+			},
+		}
+
+		cc_library {
+			name: "libz",
+			shared_libs: ["libx"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	expectLink := func(from, from_variant, to, to_variant string) {
+		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
+	}
+	expectNoLink := func(from, from_variant, to, to_variant string) {
+		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
+	}
+	expectLink("libz", "shared", "libx", "shared_2")
+	expectNoLink("libz", "shared", "libz", "shared_1")
+	expectNoLink("libz", "shared", "libz", "shared")
+}
+
+func TestQApexesUseLatestStubsInBundledBuilds(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libx"],
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libx",
+			shared_libs: ["libbar"],
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "libbar",
+			stubs: {
+				versions: ["29", "30"],
+			},
+		}
+	`)
+	expectLink := func(from, from_variant, to, to_variant string) {
+		ld := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld")
+		libFlags := ld.Args["libFlags"]
+		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
+	}
+	expectLink("libx", "shared_myapex", "libbar", "shared_30")
+}
+
+func TestQTargetApexUseStaticUnwinder(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libx"],
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libx",
+			apex_available: [ "myapex" ],
+		}
+
+	`, withUnbundledBuild)
+
+	// ensure apex variant of c++ is linked with static unwinder
+	cm := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared_myapex").Module().(*cc.Module)
+	ensureListContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped")
+	// note that platform variant is not.
+	cm = ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared").Module().(*cc.Module)
+	ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped")
+
+	libFlags := ctx.ModuleForTests("libx", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]
+	ensureContains(t, libFlags, "android_arm64_armv8-a_shared_myapex/libc++.so")
+	ensureContains(t, libFlags, "android_arm64_armv8-a_shared_29/libc.so") // min_sdk_version applied
+}
+
+func TestInvalidMinSdkVersion(t *testing.T) {
+	testApexError(t, `"libz" .*: min_sdk_version is set 29.*`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libx"],
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libx",
+			shared_libs: ["libz"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "libz",
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["30"],
+			},
+		}
+	`, withUnbundledBuild)
+
+	testApexError(t, `"myapex" .*: min_sdk_version: should be .*`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			min_sdk_version: "R",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+}
+
 func TestFilesInSubDir(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -1389,13 +1680,13 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			native_shared_libs: ["mylib"],
+			native_shared_libs: ["mylib", "mylib2"],
 		}
 
 		apex {
 			name: "otherapex",
 			key: "myapex.key",
-			native_shared_libs: ["mylib"],
+			native_shared_libs: ["mylib", "mylib2"],
 		}
 
 		apex_key {
@@ -1409,29 +1700,53 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
-			// TODO: remove //apex_available:platform
 			apex_available: [
-				"//apex_available:platform",
 				"myapex",
 				"otherapex",
 			],
 		}
+		cc_library {
+			name: "mylib2",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"myapex",
+				"otherapex",
+			],
+			use_apex_name_macro: true,
+		}
 	`)
 
-	// non-APEX variant does not have __ANDROID_APEX(_NAME)__ defined
+	// non-APEX variant does not have __ANDROID_APEX__ defined
 	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
+
+	// APEX variant has __ANDROID_APEX__ defined
+	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
+
+	// APEX variant has __ANDROID_APEX__ defined
+	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_otherapex").Rule("cc").Args["cFlags"]
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
 
-	// APEX variant has __ANDROID_APEX(_NAME)__ defined
-	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
+	// When cc_library sets use_apex_name_macro: true
+	// apex variants define additional macro to distinguish which apex variant it is built for
+
+	// non-APEX variant does not have __ANDROID_APEX__ defined
+	mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
+
+	// APEX variant has __ANDROID_APEX__ defined
+	mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
 
-	// APEX variant has __ANDROID_APEX(_NAME)__ defined
-	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_otherapex").Rule("cc").Args["cFlags"]
+	// APEX variant has __ANDROID_APEX__ defined
+	mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_otherapex").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
@@ -3030,7 +3345,7 @@
 
 func TestApexPropertiesShouldBeDefaultable(t *testing.T) {
 	// libfoo's apex_available comes from cc_defaults
-	testApexError(t, `"myapex" .*: requires "libfoo" that is not available for the APEX`, `
+	testApexError(t, `"myapex" .*: "myapex" requires "libfoo" that is not available for the APEX`, `
 	apex {
 		name: "myapex",
 		key: "myapex.key",
@@ -3301,7 +3616,7 @@
 			base: "app",
 			package_name: "bar",
 		}
-	`)
+	`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
 
 	originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule)
 	overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex_image").Module().(android.OverridableModule)
@@ -3329,6 +3644,9 @@
 		t.Errorf("override_myapex should have logging parent (com.foo.bar), but was %q.", apexBundle.overridableProperties.Logging_parent)
 	}
 
+	optFlags := apexRule.Args["opt_flags"]
+	ensureContains(t, optFlags, "--override_apk_package_name com.android.myapex")
+
 	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
 	var builder strings.Builder
 	data.Custom(&builder, name, "TARGET_", "", data)
diff --git a/apex/builder.go b/apex/builder.go
index 38a2a53..279445b 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -660,7 +660,7 @@
 		}
 		return ""
 	}
-	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(a.Name())
+	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
 	if overridden {
 		return manifestPackageName
 	}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 81004da..ef695b0 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -244,6 +244,9 @@
 	if library.shared() && !library.buildStubs() {
 		ctx.subAndroidMk(entries, library.baseInstaller)
 	} else {
+		if library.buildStubs() {
+			entries.SubName = "." + library.stubsVersion()
+		}
 		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
 			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
 			if library.buildStubs() {
@@ -254,6 +257,10 @@
 	if len(library.Properties.Stubs.Versions) > 0 &&
 		android.DirectlyInAnyApex(ctx, ctx.Name()) && !ctx.InRamdisk() && !ctx.InRecovery() && !ctx.UseVndk() &&
 		!ctx.static() {
+		if library.buildStubs() && library.isLatestStubVersion() {
+			// reference the latest version via its name without suffix when it is provided by apex
+			entries.SubName = ""
+		}
 		if !library.buildStubs() {
 			entries.SubName = ".bootstrap"
 		}
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index 58d6ad0..fc9b89e 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -16,7 +16,6 @@
 
 import (
 	"path/filepath"
-	"strings"
 
 	"android/soong/android"
 	"github.com/google/blueprint"
@@ -64,65 +63,13 @@
 	return false
 }
 
-func (mt *binarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
-	info := mt.organizeVariants(member)
-	buildSharedNativeBinarySnapshot(info, builder, member)
-}
-
-// Organize the variants by architecture.
-func (mt *binarySdkMemberType) organizeVariants(member android.SdkMember) *nativeBinaryInfo {
-	memberName := member.Name()
-	info := &nativeBinaryInfo{
-		name:       memberName,
-		memberType: mt,
-	}
-
-	for _, variant := range member.Variants() {
-		ccModule := variant.(*Module)
-
-		info.archVariantProperties = append(info.archVariantProperties, nativeBinaryInfoProperties{
-			name:       memberName,
-			archType:   ccModule.Target().Arch.ArchType.String(),
-			outputFile: ccModule.OutputFile().Path(),
-		})
-	}
-
-	// Initialize the unexported properties that will not be set during the
-	// extraction process.
-	info.commonProperties.name = memberName
-
-	// Extract common properties from the arch specific properties.
-	extractCommonProperties(&info.commonProperties, info.archVariantProperties)
-
-	return info
-}
-
-func buildSharedNativeBinarySnapshot(info *nativeBinaryInfo, builder android.SnapshotBuilder, member android.SdkMember) {
+func (mt *binarySdkMemberType) AddPrebuiltModule(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) android.BpModule {
 	pbm := builder.AddPrebuiltModule(member, "cc_prebuilt_binary")
-	archVariantCount := len(info.archVariantProperties)
+	return pbm
+}
 
-	// Choose setting for compile_multilib that is appropriate for the arch variants supplied.
-	var multilib string
-	if archVariantCount == 2 {
-		multilib = "both"
-	} else if archVariantCount == 1 {
-		if strings.HasSuffix(info.archVariantProperties[0].archType, "64") {
-			multilib = "64"
-		} else {
-			multilib = "32"
-		}
-	}
-	if multilib != "" {
-		pbm.AddProperty("compile_multilib", multilib)
-	}
-
-	archProperties := pbm.AddPropertySet("arch")
-	for _, av := range info.archVariantProperties {
-		archTypeProperties := archProperties.AddPropertySet(av.archType)
-		archTypeProperties.AddProperty("srcs", []string{nativeBinaryPathFor(av)})
-
-		builder.CopyToSnapshot(av.outputFile, nativeBinaryPathFor(av))
-	}
+func (mt *binarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+	return &nativeBinaryInfoProperties{}
 }
 
 const (
@@ -131,7 +78,7 @@
 
 // path to the native binary. Relative to <sdk_root>/<api_dir>
 func nativeBinaryPathFor(lib nativeBinaryInfoProperties) string {
-	return filepath.Join(lib.archType,
+	return filepath.Join(lib.OsPrefix(), lib.archType,
 		nativeBinaryDir, lib.outputFile.Base())
 }
 
@@ -140,8 +87,7 @@
 // The exported (capitalized) fields will be examined and may be changed during common value extraction.
 // The unexported fields will be left untouched.
 type nativeBinaryInfoProperties struct {
-	// The name of the library, is not exported as this must not be changed during optimization.
-	name string
+	android.SdkMemberPropertiesBase
 
 	// archType is not exported as if set (to a non default value) it is always arch specific.
 	// This is "" for common properties.
@@ -151,10 +97,21 @@
 	outputFile android.Path
 }
 
-// nativeBinaryInfo represents a collection of arch-specific modules having the same name
-type nativeBinaryInfo struct {
-	name                  string
-	memberType            *binarySdkMemberType
-	archVariantProperties []nativeBinaryInfoProperties
-	commonProperties      nativeBinaryInfoProperties
+func (p *nativeBinaryInfoProperties) PopulateFromVariant(variant android.SdkAware) {
+	ccModule := variant.(*Module)
+
+	p.archType = ccModule.Target().Arch.ArchType.String()
+	p.outputFile = ccModule.OutputFile().Path()
+}
+
+func (p *nativeBinaryInfoProperties) AddToPropertySet(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, propertySet android.BpPropertySet) {
+	if p.Compile_multilib != "" {
+		propertySet.AddProperty("compile_multilib", p.Compile_multilib)
+	}
+
+	if p.outputFile != nil {
+		propertySet.AddProperty("srcs", []string{nativeBinaryPathFor(*p)})
+
+		builder.CopyToSnapshot(p.outputFile, nativeBinaryPathFor(*p))
+	}
 }
diff --git a/cc/cc.go b/cc/cc.go
index 76f1f96..8b3c772 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -631,6 +631,15 @@
 	panic(fmt.Errorf("SetStubsVersions called on non-library module: %q", c.BaseModuleName()))
 }
 
+func (c *Module) StubsVersion() string {
+	if c.linker != nil {
+		if library, ok := c.linker.(*libraryDecorator); ok {
+			return library.MutatedProperties.StubsVersion
+		}
+	}
+	panic(fmt.Errorf("StubsVersion called on non-library module: %q", c.BaseModuleName()))
+}
+
 func (c *Module) SetStatic() {
 	if c.linker != nil {
 		if library, ok := c.linker.(libraryInterface); ok {
@@ -1465,6 +1474,13 @@
 			c.Properties.HideFromMake = false // unhide
 			// Note: this is still non-installable
 		}
+
+		// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current.
+		if i, ok := c.linker.(snapshotLibraryInterface); ok && ctx.DeviceConfig().VndkVersion() == "current" {
+			if isSnapshotAware(ctx, c) {
+				i.collectHeadersForSnapshot(ctx)
+			}
+		}
 	}
 
 	if c.installable() {
@@ -1814,16 +1830,17 @@
 		}
 		actx.AddVariationDependencies(variations, depTag, name)
 
-		// If the version is not specified, add dependency to the latest stubs library.
+		// If the version is not specified, add dependency to all stubs libraries.
 		// The stubs library will be used when the depending module is built for APEX and
 		// the dependent module is not in the same APEX.
-		latestVersion := LatestStubsVersionFor(actx.Config(), name)
-		if version == "" && latestVersion != "" && versionVariantAvail {
-			actx.AddVariationDependencies([]blueprint.Variation{
-				{Mutator: "link", Variation: "shared"},
-				{Mutator: "version", Variation: latestVersion},
-			}, depTag, name)
-			// Note that depTag.ExplicitlyVersioned is false in this case.
+		if version == "" && versionVariantAvail {
+			for _, ver := range stubsVersionsFor(actx.Config())[name] {
+				// Note that depTag.ExplicitlyVersioned is false in this case.
+				actx.AddVariationDependencies([]blueprint.Variation{
+					{Mutator: "link", Variation: "shared"},
+					{Mutator: "version", Variation: ver},
+				}, depTag, name)
+			}
 		}
 	}
 
@@ -2171,7 +2188,8 @@
 		}
 
 		if depTag == staticUnwinderDepTag {
-			if c.ApexProperties.Info.LegacyAndroid10Support {
+			// Use static unwinder for legacy (min_sdk_version = 29) apexes  (b/144430859)
+			if c.ShouldSupportAndroid10() {
 				depTag = StaticDepTag
 			} else {
 				return
@@ -2223,6 +2241,19 @@
 					useThisDep = (depInSameApex != depIsStubs)
 				}
 
+				// when to use (unspecified) stubs, check min_sdk_version and choose the right one
+				if useThisDep && depIsStubs && !explicitlyVersioned {
+					useLatest := c.IsForPlatform() || (c.ShouldSupportAndroid10() && !ctx.Config().UnbundledBuild())
+					versionToUse, err := c.ChooseSdkVersion(ccDep.StubsVersions(), useLatest)
+					if err != nil {
+						ctx.OtherModuleErrorf(dep, err.Error())
+						return
+					}
+					if versionToUse != ccDep.StubsVersion() {
+						useThisDep = false
+					}
+				}
+
 				if !useThisDep {
 					return // stop processing this dep
 				}
@@ -2740,19 +2771,18 @@
 
 	if vndkdep := m.vndkdep; vndkdep != nil {
 		if vndkdep.isVndk() {
-			if productSpecific {
-				mctx.PropertyErrorf("product_specific",
-					"product_specific must not be true when `vndk: {enabled: true}`")
-			}
-			if vendorSpecific {
+			if vendorSpecific || productSpecific {
 				if !vndkdep.isVndkExt() {
 					mctx.PropertyErrorf("vndk",
 						"must set `extends: \"...\"` to vndk extension")
+				} else if m.VendorProperties.Vendor_available != nil {
+					mctx.PropertyErrorf("vendor_available",
+						"must not set at the same time as `vndk: {extends: \"...\"}`")
 				}
 			} else {
 				if vndkdep.isVndkExt() {
 					mctx.PropertyErrorf("vndk",
-						"must set `vendor: true` to set `extends: %q`",
+						"must set `vendor: true` or `product_specific: true` to set `extends: %q`",
 						m.getVndkExtendsModuleName())
 				}
 				if m.VendorProperties.Vendor_available == nil {
@@ -2825,7 +2855,7 @@
 		} else {
 			mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
 		}
-	} else if m.HasVendorVariant() && !vendorSpecific {
+	} else if m.HasVendorVariant() && !m.isVndkExt() {
 		// This will be available in /system, /vendor and /product
 		// or a /system directory that is available to vendor and product.
 		coreVariantNeeded = true
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 30ba733..56b36df 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -218,13 +218,13 @@
 }
 
 func checkVndkModule(t *testing.T, ctx *android.TestContext, name, subDir string,
-	isVndkSp bool, extends string) {
+	isVndkSp bool, extends string, variant string) {
 
 	t.Helper()
 
-	mod := ctx.ModuleForTests(name, vendorVariant).Module().(*Module)
+	mod := ctx.ModuleForTests(name, variant).Module().(*Module)
 	if !mod.HasVendorVariant() {
-		t.Errorf("%q must have vendor variant", name)
+		t.Errorf("%q must have variant %q", name, variant)
 	}
 
 	// Check library properties.
@@ -375,10 +375,10 @@
 
 	ctx := testCcWithConfig(t, config)
 
-	checkVndkModule(t, ctx, "libvndk", "vndk-VER", false, "")
-	checkVndkModule(t, ctx, "libvndk_private", "vndk-VER", false, "")
-	checkVndkModule(t, ctx, "libvndk_sp", "vndk-sp-VER", true, "")
-	checkVndkModule(t, ctx, "libvndk_sp_private", "vndk-sp-VER", true, "")
+	checkVndkModule(t, ctx, "libvndk", "vndk-VER", false, "", vendorVariant)
+	checkVndkModule(t, ctx, "libvndk_private", "vndk-VER", false, "", vendorVariant)
+	checkVndkModule(t, ctx, "libvndk_sp", "vndk-sp-VER", true, "", vendorVariant)
+	checkVndkModule(t, ctx, "libvndk_sp_private", "vndk-sp-VER", true, "", vendorVariant)
 
 	// Check VNDK snapshot output.
 
@@ -1001,24 +1001,9 @@
 	`)
 }
 
-func TestVndkMustNotBeProductSpecific(t *testing.T) {
-	// Check whether an error is emitted when a vndk lib has 'product_specific: true'.
-	testCcError(t, "product_specific must not be true when `vndk: {enabled: true}`", `
-		cc_library {
-			name: "libvndk",
-			product_specific: true,  // Cause error
-			vendor_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-	`)
-}
-
 func TestVndkExt(t *testing.T) {
 	// This test checks the VNDK-Ext properties.
-	ctx := testCc(t, `
+	bp := `
 		cc_library {
 			name: "libvndk",
 			vendor_available: true,
@@ -1060,12 +1045,42 @@
 			},
 			nocrt: true,
 		}
-	`)
 
-	checkVndkModule(t, ctx, "libvndk_ext", "vndk", false, "libvndk")
+		cc_library {
+			name: "libvndk_ext_product",
+			product_specific: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			nocrt: true,
+		}
 
-	mod := ctx.ModuleForTests("libvndk2_ext", vendorVariant).Module().(*Module)
-	assertString(t, mod.outputFile.Path().Base(), "libvndk2-suffix.so")
+		cc_library {
+			name: "libvndk2_ext_product",
+			product_specific: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk2",
+			},
+			nocrt: true,
+		}
+	`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.ProductVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+
+	ctx := testCcWithConfig(t, config)
+
+	checkVndkModule(t, ctx, "libvndk_ext", "vndk", false, "libvndk", vendorVariant)
+	checkVndkModule(t, ctx, "libvndk_ext_product", "vndk", false, "libvndk", productVariant)
+
+	mod_vendor := ctx.ModuleForTests("libvndk2_ext", vendorVariant).Module().(*Module)
+	assertString(t, mod_vendor.outputFile.Path().Base(), "libvndk2-suffix.so")
+
+	mod_product := ctx.ModuleForTests("libvndk2_ext_product", productVariant).Module().(*Module)
+	assertString(t, mod_product.outputFile.Path().Base(), "libvndk2-suffix.so")
 }
 
 func TestVndkExtWithoutBoardVndkVersion(t *testing.T) {
@@ -1098,9 +1113,39 @@
 	}
 }
 
+func TestVndkExtWithoutProductVndkVersion(t *testing.T) {
+	// This test checks the VNDK-Ext properties when PRODUCT_PRODUCT_VNDK_VERSION is not set.
+	ctx := testCc(t, `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext_product",
+			product_specific: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			nocrt: true,
+		}
+	`)
+
+	// Ensures that the core variant of "libvndk_ext_product" can be found.
+	mod := ctx.ModuleForTests("libvndk_ext_product", coreVariant).Module().(*Module)
+	if extends := mod.getVndkExtendsModuleName(); extends != "libvndk" {
+		t.Errorf("\"libvndk_ext_product\" must extend from \"libvndk\" but get %q", extends)
+	}
+}
+
 func TestVndkExtError(t *testing.T) {
 	// This test ensures an error is emitted in ill-formed vndk-ext definition.
-	testCcError(t, "must set `vendor: true` to set `extends: \".*\"`", `
+	testCcError(t, "must set `vendor: true` or `product_specific: true` to set `extends: \".*\"`", `
 		cc_library {
 			name: "libvndk",
 			vendor_available: true,
@@ -1139,6 +1184,48 @@
 			nocrt: true,
 		}
 	`)
+
+	testCcErrorProductVndk(t, "must set `extends: \"\\.\\.\\.\"` to vndk extension", `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext_product",
+			product_specific: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+	`)
+
+	testCcErrorProductVndk(t, "must not set at the same time as `vndk: {extends: \"\\.\\.\\.\"}`", `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext_product",
+			product_specific: true,
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			nocrt: true,
+		}
+	`)
 }
 
 func TestVndkExtInconsistentSupportSystemProcessError(t *testing.T) {
@@ -1211,6 +1298,27 @@
 			nocrt: true,
 		}
 	`)
+
+	testCcErrorProductVndk(t, "`extends` refers module \".*\" which does not have `vendor_available: true`", `
+		cc_library {
+			name: "libvndk",
+			vendor_available: false,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext_product",
+			product_specific: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			nocrt: true,
+		}
+	`)
 }
 
 func TestVendorModuleUseVndkExt(t *testing.T) {
@@ -1236,7 +1344,6 @@
 		}
 
 		cc_library {
-
 			name: "libvndk_sp",
 			vendor_available: true,
 			vndk: {
@@ -1328,6 +1435,71 @@
 	`)
 }
 
+func TestProductVndkExtDependency(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext_product",
+			product_specific: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			shared_libs: ["libproduct_for_vndklibs"],
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp_ext_product",
+			product_specific: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk_sp",
+				support_system_process: true,
+			},
+			shared_libs: ["libproduct_for_vndklibs"],
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libproduct",
+			product_specific: true,
+			shared_libs: ["libvndk_ext_product", "libvndk_sp_ext_product"],
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libproduct_for_vndklibs",
+			product_specific: true,
+			nocrt: true,
+		}
+	`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.ProductVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+
+	testCcWithConfig(t, config)
+}
+
 func TestVndkSpExtUseVndkError(t *testing.T) {
 	// This test ensures an error is emitted if a VNDK-SP-Ext library depends on a VNDK
 	// library.
@@ -1619,8 +1791,8 @@
 
 	ctx := testCcWithConfig(t, config)
 
-	checkVndkModule(t, ctx, "libvndk", "vndk-VER", false, "")
-	checkVndkModule(t, ctx, "libvndk_sp", "vndk-sp-VER", true, "")
+	checkVndkModule(t, ctx, "libvndk", "vndk-VER", false, "", productVariant)
+	checkVndkModule(t, ctx, "libvndk_sp", "vndk-sp-VER", true, "", productVariant)
 }
 
 func TestEnforceProductVndkVersionErrors(t *testing.T) {
@@ -2358,6 +2530,7 @@
 }
 
 func checkStaticLibs(t *testing.T, expected []string, module *Module) {
+	t.Helper()
 	actual := module.Properties.AndroidMkStaticLibs
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("incorrect static_libs"+
diff --git a/cc/compiler.go b/cc/compiler.go
index c1a8d96..3a87b69 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -176,6 +176,9 @@
 
 	// Build and link with OpenMP
 	Openmp *bool `android:"arch_variant"`
+
+	// Adds __ANDROID_APEX_<APEX_MODULE_NAME>__ macro defined for apex variants in addition to __ANDROID_APEX__
+	Use_apex_name_macro *bool
 }
 
 func NewBaseCompiler() *baseCompiler {
@@ -321,9 +324,10 @@
 	}
 
 	if ctx.apexName() != "" {
-		flags.Global.CommonFlags = append(flags.Global.CommonFlags,
-			"-D__ANDROID_APEX__",
-			"-D__ANDROID_APEX_"+makeDefineString(ctx.apexName())+"__")
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX__")
+		if Bool(compiler.Properties.Use_apex_name_macro) {
+			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX_"+makeDefineString(ctx.apexName())+"__")
+		}
 	}
 
 	instructionSet := String(compiler.Properties.Instruction_set)
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 1ca1656..19aedd9 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -39,6 +39,7 @@
 	arm64Ldflags = []string{
 		"-Wl,-m,aarch64_elf64_le_vec",
 		"-Wl,--hash-style=gnu",
+		"-Wl,-z,separate-code",
 		"-fuse-ld=gold",
 		"-Wl,--icf=safe",
 	}
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index dd52a0e..4ac9e58 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -30,10 +30,12 @@
 		}
 		return strings.Join([]string{
 			"-*",
+			"bugprone*",
 			"clang-diagnostic-unused-command-line-argument",
 			"google*",
 			"misc-macro-parentheses",
 			"performance*",
+			"-bugprone-narrowing-conversions",
 			"-google-readability*",
 			"-google-runtime-references",
 		}, ",")
diff --git a/cc/library.go b/cc/library.go
index 6ffb7fc..e1d0b34 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -379,6 +379,68 @@
 	*baseCompiler
 	*baseLinker
 	*baseInstaller
+
+	collectedSnapshotHeaders android.Paths
+}
+
+// collectHeadersForSnapshot collects all exported headers from library.
+// It globs header files in the source tree for exported include directories,
+// and tracks generated header files separately.
+//
+// This is to be called from GenerateAndroidBuildActions, and then collected
+// header files can be retrieved by snapshotHeaders().
+func (l *libraryDecorator) collectHeadersForSnapshot(ctx android.ModuleContext) {
+	ret := android.Paths{}
+
+	// Headers in the source tree should be globbed. On the contrast, generated headers
+	// can't be globbed, and they should be manually collected.
+	// So, we first filter out intermediate directories (which contains generated headers)
+	// from exported directories, and then glob headers under remaining directories.
+	for _, path := range append(l.exportedDirs(), l.exportedSystemDirs()...) {
+		dir := path.String()
+		// Skip if dir is for generated headers
+		if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
+			continue
+		}
+		exts := headerExts
+		// Glob all files under this special directory, because of C++ headers.
+		if strings.HasPrefix(dir, "external/libcxx/include") {
+			exts = []string{""}
+		}
+		for _, ext := range exts {
+			glob, err := ctx.GlobWithDeps(dir+"/**/*"+ext, nil)
+			if err != nil {
+				ctx.ModuleErrorf("glob failed: %#v", err)
+				return
+			}
+			for _, header := range glob {
+				if strings.HasSuffix(header, "/") {
+					continue
+				}
+				ret = append(ret, android.PathForSource(ctx, header))
+			}
+		}
+	}
+
+	// Collect generated headers
+	for _, header := range append(l.exportedGeneratedHeaders(), l.exportedDeps()...) {
+		// TODO(b/148123511): remove exportedDeps after cleaning up genrule
+		if strings.HasSuffix(header.Base(), "-phony") {
+			continue
+		}
+		ret = append(ret, header)
+	}
+
+	l.collectedSnapshotHeaders = ret
+}
+
+// This returns all exported header files, both generated ones and headers from source tree.
+// collectHeadersForSnapshot() must be called before calling this.
+func (l *libraryDecorator) snapshotHeaders() android.Paths {
+	if l.collectedSnapshotHeaders == nil {
+		panic("snapshotHeaders() must be called after collectHeadersForSnapshot()")
+	}
+	return l.collectedSnapshotHeaders
 }
 
 func (library *libraryDecorator) linkerProps() []interface{} {
@@ -1211,6 +1273,11 @@
 	return library.MutatedProperties.StubsVersion
 }
 
+func (library *libraryDecorator) isLatestStubVersion() bool {
+	versions := library.Properties.Stubs.Versions
+	return versions[len(versions)-1] == library.stubsVersion()
+}
+
 func (library *libraryDecorator) availableFor(what string) bool {
 	var list []string
 	if library.static() {
@@ -1382,30 +1449,35 @@
 	return ""
 }
 
+func checkVersions(ctx android.BaseModuleContext, versions []string) {
+	numVersions := make([]int, len(versions))
+	for i, v := range versions {
+		numVer, err := strconv.Atoi(v)
+		if err != nil {
+			ctx.PropertyErrorf("versions", "%q is not a number", v)
+		}
+		numVersions[i] = numVer
+	}
+	if !sort.IsSorted(sort.IntSlice(numVersions)) {
+		ctx.PropertyErrorf("versions", "not sorted: %v", versions)
+	}
+}
+
 // Version mutator splits a module into the mandatory non-stubs variant
 // (which is unnamed) and zero or more stubs variants.
 func VersionMutator(mctx android.BottomUpMutatorContext) {
 	if library, ok := mctx.Module().(LinkableInterface); ok && !library.InRecovery() {
 		if library.CcLibrary() && library.BuildSharedVariant() && len(library.StubsVersions()) > 0 {
-			versions := []string{}
-			for _, v := range library.StubsVersions() {
-				if _, err := strconv.Atoi(v); err != nil {
-					mctx.PropertyErrorf("versions", "%q is not a number", v)
-				}
-				versions = append(versions, v)
+			versions := library.StubsVersions()
+			checkVersions(mctx, versions)
+			if mctx.Failed() {
+				return
 			}
-			sort.Slice(versions, func(i, j int) bool {
-				left, _ := strconv.Atoi(versions[i])
-				right, _ := strconv.Atoi(versions[j])
-				return left < right
-			})
 
 			// save the list of versions for later use
-			copiedVersions := make([]string, len(versions))
-			copy(copiedVersions, versions)
 			stubsVersionsLock.Lock()
 			defer stubsVersionsLock.Unlock()
-			stubsVersionsFor(mctx.Config())[mctx.ModuleName()] = copiedVersions
+			stubsVersionsFor(mctx.Config())[mctx.ModuleName()] = versions
 
 			// "" is for the non-stubs variant
 			versions = append([]string{""}, versions...)
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index a36917e..656df69 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -16,7 +16,6 @@
 
 import (
 	"path/filepath"
-	"reflect"
 
 	"android/soong/android"
 	"github.com/google/blueprint"
@@ -96,48 +95,25 @@
 	return false
 }
 
-// copy exported header files and stub *.so files
-func (mt *librarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
-	info := mt.organizeVariants(member)
-	info.generatePrebuiltLibrary(sdkModuleContext, builder, member)
+func (mt *librarySdkMemberType) AddPrebuiltModule(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) android.BpModule {
+	pbm := builder.AddPrebuiltModule(member, mt.prebuiltModuleType)
+
+	ccModule := member.Variants()[0].(*Module)
+
+	sdkVersion := ccModule.SdkVersion()
+	if sdkVersion != "" {
+		pbm.AddProperty("sdk_version", sdkVersion)
+	}
+	return pbm
 }
 
-// Organize the variants by architecture.
-func (mt *librarySdkMemberType) organizeVariants(member android.SdkMember) *nativeLibInfo {
-	memberName := member.Name()
-	info := &nativeLibInfo{
-		name:       memberName,
-		memberType: mt,
-	}
+func (mt *librarySdkMemberType) FinalizeModule(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember, bpModule android.BpModule) {
+	bpModule.AddProperty("stl", "none")
+	bpModule.AddProperty("system_shared_libs", []string{})
+}
 
-	for _, variant := range member.Variants() {
-		ccModule := variant.(*Module)
-
-		// Separate out the generated include dirs (which are arch specific) from the
-		// include dirs (which may not be).
-		exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate(
-			ccModule.ExportedIncludeDirs(), isGeneratedHeaderDirectory)
-
-		info.archVariantProperties = append(info.archVariantProperties, nativeLibInfoProperties{
-			name:                         memberName,
-			archType:                     ccModule.Target().Arch.ArchType.String(),
-			ExportedIncludeDirs:          exportedIncludeDirs,
-			exportedGeneratedIncludeDirs: exportedGeneratedIncludeDirs,
-			ExportedSystemIncludeDirs:    ccModule.ExportedSystemIncludeDirs(),
-			ExportedFlags:                ccModule.ExportedFlags(),
-			exportedGeneratedHeaders:     ccModule.ExportedGeneratedHeaders(),
-			outputFile:                   ccModule.OutputFile().Path(),
-		})
-	}
-
-	// Initialize the unexported properties that will not be set during the
-	// extraction process.
-	info.commonProperties.name = memberName
-
-	// Extract common properties from the arch specific properties.
-	extractCommonProperties(&info.commonProperties, info.archVariantProperties)
-
-	return info
+func (mt *librarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+	return &nativeLibInfoProperties{memberType: mt}
 }
 
 func isGeneratedHeaderDirectory(p android.Path) bool {
@@ -145,94 +121,9 @@
 	return gen
 }
 
-// Extract common properties from a slice of property structures of the same type.
-//
-// All the property structures must be of the same type.
-// commonProperties - must be a pointer to the structure into which common properties will be added.
-// inputPropertiesSlice - must be a slice of input properties structures.
-//
-// Iterates over each exported field (capitalized name) and checks to see whether they
-// have the same value (using DeepEquals) across all the input properties. If it does not then no
-// change is made. Otherwise, the common value is stored in the field in the commonProperties
-// and the field in each of the input properties structure is set to its default value.
-func extractCommonProperties(commonProperties interface{}, inputPropertiesSlice interface{}) {
-	commonStructValue := reflect.ValueOf(commonProperties).Elem()
-	propertiesStructType := commonStructValue.Type()
-
-	// Create an empty structure from which default values for the field can be copied.
-	emptyStructValue := reflect.New(propertiesStructType).Elem()
-
-	for f := 0; f < propertiesStructType.NumField(); f++ {
-		// Check to see if all the structures have the same value for the field. The commonValue
-		// is nil on entry to the loop and if it is nil on exit then there is no common value,
-		// otherwise it points to the common value.
-		var commonValue *reflect.Value
-		sliceValue := reflect.ValueOf(inputPropertiesSlice)
-
-		for i := 0; i < sliceValue.Len(); i++ {
-			structValue := sliceValue.Index(i)
-			fieldValue := structValue.Field(f)
-			if !fieldValue.CanInterface() {
-				// The field is not exported so ignore it.
-				continue
-			}
-
-			if commonValue == nil {
-				// Use the first value as the commonProperties value.
-				commonValue = &fieldValue
-			} else {
-				// If the value does not match the current common value then there is
-				// no value in common so break out.
-				if !reflect.DeepEqual(fieldValue.Interface(), commonValue.Interface()) {
-					commonValue = nil
-					break
-				}
-			}
-		}
-
-		// If the fields all have a common value then store it in the common struct field
-		// and set the input struct's field to the empty value.
-		if commonValue != nil {
-			emptyValue := emptyStructValue.Field(f)
-			commonStructValue.Field(f).Set(*commonValue)
-			for i := 0; i < sliceValue.Len(); i++ {
-				structValue := sliceValue.Index(i)
-				fieldValue := structValue.Field(f)
-				fieldValue.Set(emptyValue)
-			}
-		}
-	}
-}
-
-func (info *nativeLibInfo) generatePrebuiltLibrary(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
-
-	pbm := builder.AddPrebuiltModule(member, info.memberType.prebuiltModuleType)
-
-	addPossiblyArchSpecificProperties(sdkModuleContext, builder, info.commonProperties, pbm)
-
-	archProperties := pbm.AddPropertySet("arch")
-	for _, av := range info.archVariantProperties {
-		archTypeProperties := archProperties.AddPropertySet(av.archType)
-
-		// If the library has some link types then it produces an output binary file, otherwise it
-		// is header only.
-		if info.memberType.linkTypes != nil {
-			// Copy the generated library to the snapshot and add a reference to it in the .bp module.
-			nativeLibraryPath := nativeLibraryPathFor(av)
-			builder.CopyToSnapshot(av.outputFile, nativeLibraryPath)
-			archTypeProperties.AddProperty("srcs", []string{nativeLibraryPath})
-		}
-
-		// Add any arch specific properties inside the appropriate arch: {<arch>: {...}} block
-		addPossiblyArchSpecificProperties(sdkModuleContext, builder, av, archTypeProperties)
-	}
-	pbm.AddProperty("stl", "none")
-	pbm.AddProperty("system_shared_libs", []string{})
-}
-
 type includeDirsProperty struct {
 	// Accessor to retrieve the paths
-	pathsGetter func(libInfo nativeLibInfoProperties) android.Paths
+	pathsGetter func(libInfo *nativeLibInfoProperties) android.Paths
 
 	// The name of the property in the prebuilt library, "" means there is no property.
 	propertyName string
@@ -252,7 +143,7 @@
 		// ExportedIncludeDirs lists directories that contains some header files to be
 		// copied into a directory in the snapshot. The snapshot directories must be added to
 		// the export_include_dirs property in the prebuilt module in the snapshot.
-		pathsGetter:  func(libInfo nativeLibInfoProperties) android.Paths { return libInfo.ExportedIncludeDirs },
+		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedIncludeDirs },
 		propertyName: "export_include_dirs",
 		snapshotDir:  nativeIncludeDir,
 		copy:         true,
@@ -262,7 +153,7 @@
 		// ExportedSystemIncludeDirs lists directories that contains some system header files to
 		// be copied into a directory in the snapshot. The snapshot directories must be added to
 		// the export_system_include_dirs property in the prebuilt module in the snapshot.
-		pathsGetter:  func(libInfo nativeLibInfoProperties) android.Paths { return libInfo.ExportedSystemIncludeDirs },
+		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedSystemIncludeDirs },
 		propertyName: "export_system_include_dirs",
 		snapshotDir:  nativeIncludeDir,
 		copy:         true,
@@ -273,7 +164,7 @@
 		// that are explicitly listed in the exportedGeneratedHeaders property. So, the contents
 		// of these directories do not need to be copied, but these directories do need adding to
 		// the export_include_dirs property in the prebuilt module in the snapshot.
-		pathsGetter:  func(libInfo nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedIncludeDirs },
+		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedIncludeDirs },
 		propertyName: "export_include_dirs",
 		snapshotDir:  nativeGeneratedIncludeDir,
 		copy:         false,
@@ -284,7 +175,7 @@
 		// specified in exportedGeneratedIncludeDirs must be copied into the snapshot.
 		// As they are in a directory in exportedGeneratedIncludeDirs they do not need adding to a
 		// property in the prebuilt module in the snapshot.
-		pathsGetter:  func(libInfo nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedHeaders },
+		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedHeaders },
 		propertyName: "",
 		snapshotDir:  nativeGeneratedIncludeDir,
 		copy:         true,
@@ -293,7 +184,14 @@
 }
 
 // Add properties that may, or may not, be arch specific.
-func addPossiblyArchSpecificProperties(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, libInfo nativeLibInfoProperties, outputProperties android.BpPropertySet) {
+func addPossiblyArchSpecificProperties(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, libInfo *nativeLibInfoProperties, outputProperties android.BpPropertySet) {
+
+	// Copy the generated library to the snapshot and add a reference to it in the .bp module.
+	if libInfo.outputFile != nil {
+		nativeLibraryPath := nativeLibraryPathFor(libInfo)
+		builder.CopyToSnapshot(libInfo.outputFile, nativeLibraryPath)
+		outputProperties.AddProperty("srcs", []string{nativeLibraryPath})
+	}
 
 	// Map from property name to the include dirs to add to the prebuilt module in the snapshot.
 	includeDirs := make(map[string][]string)
@@ -355,8 +253,8 @@
 )
 
 // path to the native library. Relative to <sdk_root>/<api_dir>
-func nativeLibraryPathFor(lib nativeLibInfoProperties) string {
-	return filepath.Join(lib.archType,
+func nativeLibraryPathFor(lib *nativeLibInfoProperties) string {
+	return filepath.Join(lib.OsPrefix(), lib.archType,
 		nativeStubDir, lib.outputFile.Base())
 }
 
@@ -365,6 +263,10 @@
 // The exported (capitalized) fields will be examined and may be changed during common value extraction.
 // The unexported fields will be left untouched.
 type nativeLibInfoProperties struct {
+	android.SdkMemberPropertiesBase
+
+	memberType *librarySdkMemberType
+
 	// The name of the library, is not exported as this must not be changed during optimization.
 	name string
 
@@ -401,10 +303,29 @@
 	outputFile android.Path
 }
 
-// nativeLibInfo represents a collection of arch-specific modules having the same name
-type nativeLibInfo struct {
-	name                  string
-	memberType            *librarySdkMemberType
-	archVariantProperties []nativeLibInfoProperties
-	commonProperties      nativeLibInfoProperties
+func (p *nativeLibInfoProperties) PopulateFromVariant(variant android.SdkAware) {
+	ccModule := variant.(*Module)
+
+	// If the library has some link types then it produces an output binary file, otherwise it
+	// is header only.
+	if p.memberType.linkTypes != nil {
+		p.outputFile = ccModule.OutputFile().Path()
+	}
+
+	// Separate out the generated include dirs (which are arch specific) from the
+	// include dirs (which may not be).
+	exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate(
+		ccModule.ExportedIncludeDirs(), isGeneratedHeaderDirectory)
+
+	p.name = variant.Name()
+	p.archType = ccModule.Target().Arch.ArchType.String()
+	p.ExportedIncludeDirs = exportedIncludeDirs
+	p.exportedGeneratedIncludeDirs = exportedGeneratedIncludeDirs
+	p.ExportedSystemIncludeDirs = ccModule.ExportedSystemIncludeDirs()
+	p.ExportedFlags = ccModule.ExportedFlags()
+	p.exportedGeneratedHeaders = ccModule.ExportedGeneratedHeaders()
+}
+
+func (p *nativeLibInfoProperties) AddToPropertySet(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, propertySet android.BpPropertySet) {
+	addPossiblyArchSpecificProperties(sdkModuleContext, builder, p, propertySet)
 }
diff --git a/cc/linkable.go b/cc/linkable.go
index e4f034c..80cd6b8 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -26,6 +26,7 @@
 	BuildStubs() bool
 	SetBuildStubs()
 	SetStubsVersions(string)
+	StubsVersion() string
 	HasStubsVariants() bool
 	SelectedStl() string
 	ApiLevel() string
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index 73388ce..4012def 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -14,8 +14,6 @@
 package cc
 
 import (
-	"strings"
-
 	"android/soong/android"
 )
 
@@ -26,6 +24,8 @@
 type snapshotLibraryInterface interface {
 	exportedFlagsProducer
 	libraryInterface
+	collectHeadersForSnapshot(ctx android.ModuleContext)
+	snapshotHeaders() android.Paths
 }
 
 var _ snapshotLibraryInterface = (*prebuiltLibraryLinker)(nil)
@@ -58,49 +58,13 @@
 	return snapshot, found
 }
 
-func exportedHeaders(ctx android.SingletonContext, l exportedFlagsProducer) android.Paths {
-	var ret android.Paths
-
-	// Headers in the source tree should be globbed. On the contrast, generated headers
-	// can't be globbed, and they should be manually collected.
-	// So, we first filter out intermediate directories (which contains generated headers)
-	// from exported directories, and then glob headers under remaining directories.
-	for _, path := range append(l.exportedDirs(), l.exportedSystemDirs()...) {
-		dir := path.String()
-		// Skip if dir is for generated headers
-		if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
-			continue
-		}
-		exts := headerExts
-		// Glob all files under this special directory, because of C++ headers.
-		if strings.HasPrefix(dir, "external/libcxx/include") {
-			exts = []string{""}
-		}
-		for _, ext := range exts {
-			glob, err := ctx.GlobWithDeps(dir+"/**/*"+ext, nil)
-			if err != nil {
-				ctx.Errorf("%#v\n", err)
-				return nil
-			}
-			for _, header := range glob {
-				if strings.HasSuffix(header, "/") {
-					continue
-				}
-				ret = append(ret, android.PathForSource(ctx, header))
-			}
-		}
+func isSnapshotAware(ctx android.ModuleContext, m *Module) bool {
+	if _, _, ok := isVndkSnapshotLibrary(ctx.DeviceConfig(), m); ok {
+		return ctx.Config().VndkSnapshotBuildArtifacts()
+	} else if isVendorSnapshotModule(m, ctx.ModuleDir()) {
+		return true
 	}
-
-	// Collect generated headers
-	for _, header := range append(l.exportedGeneratedHeaders(), l.exportedDeps()...) {
-		// TODO(b/148123511): remove exportedDeps after cleaning up genrule
-		if strings.HasSuffix(header.Base(), "-phony") {
-			continue
-		}
-		ret = append(ret, header)
-	}
-
-	return ret
+	return false
 }
 
 func copyFile(ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
diff --git a/cc/stl.go b/cc/stl.go
index eda8a4f..8113f72 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -235,11 +235,6 @@
 			flags.Local.CppFlags = append(flags.Local.CppFlags, "-nostdinc++")
 			flags.extraLibFlags = append(flags.extraLibFlags, "-nostdlib++")
 			if ctx.Windows() {
-				if stl.Properties.SelectedStl == "libc++_static" {
-					// These are transitively needed by libc++_static.
-					flags.extraLibFlags = append(flags.extraLibFlags,
-						"-lmsvcrt", "-lucrt")
-				}
 				// Use SjLj exceptions for 32-bit.  libgcc_eh implements SjLj
 				// exception model for 32-bit.
 				if ctx.Arch().ArchType == android.X86 {
diff --git a/cc/testing.go b/cc/testing.go
index a22763a..b8a7eab 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -34,7 +34,7 @@
 	ctx.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
 }
 
-func GatherRequiredDepsForTest(os android.OsType) string {
+func GatherRequiredDepsForTest(oses ...android.OsType) string {
 	ret := `
 		toolchain_library {
 			name: "libatomic",
@@ -341,8 +341,9 @@
 		}
 	`
 
-	if os == android.Fuchsia {
-		ret += `
+	for _, os := range oses {
+		if os == android.Fuchsia {
+			ret += `
 		cc_library {
 			name: "libbioniccompat",
 			stl: "none",
@@ -352,6 +353,22 @@
 			stl: "none",
 		}
 		`
+		}
+		if os == android.Windows {
+			ret += `
+		toolchain_library {
+			name: "libwinpthread",
+			host_supported: true,
+			enabled: false,
+			target: {
+				windows: {
+					enabled: true,
+				},
+			},
+			src: "",
+		}
+		`
+		}
 	}
 	return ret
 }
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 20762a8..5801fc7 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -428,12 +428,12 @@
 // AOSP. They are not guaranteed to be compatible with older vendor images. (e.g. might
 // depend on newer VNDK) So they are captured as vendor snapshot To build older vendor
 // image and newer system image altogether.
-func isVendorSnapshotModule(ctx android.SingletonContext, m *Module) bool {
+func isVendorSnapshotModule(m *Module, moduleDir string) bool {
 	if !m.Enabled() {
 		return false
 	}
 	// skip proprietary modules, but include all VNDK (static)
-	if isVendorProprietaryPath(ctx.ModuleDir(m)) && !m.IsVndk() {
+	if isVendorProprietaryPath(moduleDir) && !m.IsVndk() {
 		return false
 	}
 	if m.Target().Os.Class != android.Device {
@@ -525,14 +525,6 @@
 
 	var headers android.Paths
 
-	type vendorSnapshotLibraryInterface interface {
-		exportedFlagsProducer
-		libraryInterface
-	}
-
-	var _ vendorSnapshotLibraryInterface = (*prebuiltLibraryLinker)(nil)
-	var _ vendorSnapshotLibraryInterface = (*libraryDecorator)(nil)
-
 	installSnapshot := func(m *Module) android.Paths {
 		targetArch := "arch-" + m.Target().Arch.ArchType.String()
 		if m.Target().Arch.ArchVariant != "" {
@@ -588,7 +580,7 @@
 
 		var propOut string
 
-		if l, ok := m.linker.(vendorSnapshotLibraryInterface); ok {
+		if l, ok := m.linker.(snapshotLibraryInterface); ok {
 			// library flags
 			prop.ExportedFlags = l.exportedFlags()
 			for _, dir := range l.exportedDirs() {
@@ -652,13 +644,18 @@
 
 	ctx.VisitAllModules(func(module android.Module) {
 		m, ok := module.(*Module)
-		if !ok || !isVendorSnapshotModule(ctx, m) {
+		if !ok {
+			return
+		}
+
+		moduleDir := ctx.ModuleDir(module)
+		if !isVendorSnapshotModule(m, moduleDir) {
 			return
 		}
 
 		snapshotOutputs = append(snapshotOutputs, installSnapshot(m)...)
-		if l, ok := m.linker.(vendorSnapshotLibraryInterface); ok {
-			headers = append(headers, exportedHeaders(ctx, l)...)
+		if l, ok := m.linker.(snapshotLibraryInterface); ok {
+			headers = append(headers, l.snapshotHeaders()...)
 		}
 
 		if len(m.NoticeFiles()) > 0 {
diff --git a/cc/vndk.go b/cc/vndk.go
index d0492fc..e02e7b5 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -496,6 +496,28 @@
 	vndkSnapshotZipFile android.OptionalPath
 }
 
+func isVndkSnapshotLibrary(config android.DeviceConfig, m *Module) (i snapshotLibraryInterface, vndkType string, isVndkSnapshotLib bool) {
+	if m.Target().NativeBridge == android.NativeBridgeEnabled {
+		return nil, "", false
+	}
+	if !m.inVendor() || !m.installable() || m.isSnapshotPrebuilt() {
+		return nil, "", false
+	}
+	l, ok := m.linker.(snapshotLibraryInterface)
+	if !ok || !l.shared() {
+		return nil, "", false
+	}
+	if m.VndkVersion() == config.PlatformVndkVersion() && m.IsVndk() && !m.isVndkExt() {
+		if m.isVndkSp() {
+			return l, "vndk-sp", true
+		} else {
+			return l, "vndk-core", true
+		}
+	}
+
+	return nil, "", false
+}
+
 func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	// build these files even if PlatformVndkVersion or BoardVndkVersion is not set
 	c.buildVndkLibrariesTxtFiles(ctx)
@@ -598,35 +620,13 @@
 		return ret, true
 	}
 
-	isVndkSnapshotLibrary := func(m *Module) (i snapshotLibraryInterface, vndkType string, isVndkSnapshotLib bool) {
-		if m.Target().NativeBridge == android.NativeBridgeEnabled {
-			return nil, "", false
-		}
-		if !m.inVendor() || !m.installable() || m.isSnapshotPrebuilt() {
-			return nil, "", false
-		}
-		l, ok := m.linker.(snapshotLibraryInterface)
-		if !ok || !l.shared() {
-			return nil, "", false
-		}
-		if m.VndkVersion() == ctx.DeviceConfig().PlatformVndkVersion() && m.IsVndk() && !m.isVndkExt() {
-			if m.isVndkSp() {
-				return l, "vndk-sp", true
-			} else {
-				return l, "vndk-core", true
-			}
-		}
-
-		return nil, "", false
-	}
-
 	ctx.VisitAllModules(func(module android.Module) {
 		m, ok := module.(*Module)
 		if !ok || !m.Enabled() {
 			return
 		}
 
-		l, vndkType, ok := isVndkSnapshotLibrary(m)
+		l, vndkType, ok := isVndkSnapshotLibrary(ctx.DeviceConfig(), m)
 		if !ok {
 			return
 		}
@@ -655,7 +655,7 @@
 		}
 
 		if ctx.Config().VndkSnapshotBuildArtifacts() {
-			headers = append(headers, exportedHeaders(ctx, l)...)
+			headers = append(headers, l.snapshotHeaders()...)
 		}
 	})
 
diff --git a/java/builder.go b/java/builder.go
index f9b5367..3a0247d 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -38,7 +38,8 @@
 	// this, all java rules write into separate directories and then are combined into a .jar file
 	// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
 	// .srcjar files are unzipped into a temporary directory when compiled with javac.
-	javac = pctx.AndroidRemoteStaticRule("javac", android.RemoteRuleSupports{Goma: true, RBE: true, RBEFlag: android.RBE_JAVAC},
+	// TODO(b/143658984): goma can't handle the --system argument to javac.
+	javac = pctx.AndroidRemoteStaticRule("javac", android.RemoteRuleSupports{Goma: false, RBE: true, RBEFlag: android.RBE_JAVAC},
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
 				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index d7adb40..6ec919c 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -379,6 +379,7 @@
 		FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]).
 		FlagWithArg("--android-root=", global.EmptyDirectory).
 		FlagWithArg("--no-inline-from=", "core-oj.jar").
+		Flag("--force-determinism").
 		Flag("--abort-on-hard-verifier-error")
 
 	if global.BootFlags != "" {
diff --git a/java/java.go b/java/java.go
index 8d58a90..d0b5adf 100644
--- a/java/java.go
+++ b/java/java.go
@@ -41,11 +41,17 @@
 	// Register sdk member types.
 	android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType)
 
-	android.RegisterSdkMemberType(&implLibrarySdkMemberType{
-		librarySdkMemberType{
-			android.SdkMemberTypeBase{
-				PropertyName: "java_libs",
-			},
+	android.RegisterSdkMemberType(&librarySdkMemberType{
+		android.SdkMemberTypeBase{
+			PropertyName: "java_libs",
+		},
+		func(j *Library) android.Path {
+			implementationJars := j.ImplementationJars()
+			if len(implementationJars) != 1 {
+				panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
+			}
+
+			return implementationJars[0]
 		},
 	})
 
@@ -1873,16 +1879,20 @@
 )
 
 // path to the jar file of a java library. Relative to <sdk_root>/<api_dir>
-func sdkSnapshotFilePathForJar(member android.SdkMember) string {
-	return sdkSnapshotFilePathForMember(member, jarFileSuffix)
+func sdkSnapshotFilePathForJar(osPrefix, name string) string {
+	return sdkSnapshotFilePathForMember(osPrefix, name, jarFileSuffix)
 }
 
-func sdkSnapshotFilePathForMember(member android.SdkMember, suffix string) string {
-	return filepath.Join(javaDir, member.Name()+suffix)
+func sdkSnapshotFilePathForMember(osPrefix, name string, suffix string) string {
+	return filepath.Join(javaDir, osPrefix, name+suffix)
 }
 
 type librarySdkMemberType struct {
 	android.SdkMemberTypeBase
+
+	// Function to retrieve the appropriate output jar (implementation or header) from
+	// the library.
+	jarToExportGetter func(j *Library) android.Path
 }
 
 func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
@@ -1894,75 +1904,61 @@
 	return ok
 }
 
-func (mt *librarySdkMemberType) buildSnapshot(
-	sdkModuleContext android.ModuleContext,
-	builder android.SnapshotBuilder,
-	member android.SdkMember,
-	jarToExportGetter func(j *Library) android.Path) {
+func (mt *librarySdkMemberType) AddPrebuiltModule(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) android.BpModule {
+	return builder.AddPrebuiltModule(member, "java_import")
+}
 
-	variants := member.Variants()
-	if len(variants) != 1 {
-		sdkModuleContext.ModuleErrorf("sdk contains %d variants of member %q but only one is allowed", len(variants), member.Name())
-		for _, variant := range variants {
-			sdkModuleContext.ModuleErrorf("    %q", variant)
-		}
-	}
-	variant := variants[0]
+func (mt *librarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+	return &librarySdkMemberProperties{memberType: mt}
+}
+
+type librarySdkMemberProperties struct {
+	android.SdkMemberPropertiesBase
+
+	memberType *librarySdkMemberType
+
+	library     *Library
+	jarToExport android.Path
+}
+
+func (p *librarySdkMemberProperties) PopulateFromVariant(variant android.SdkAware) {
 	j := variant.(*Library)
 
-	exportedJar := jarToExportGetter(j)
-	snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(member)
-	builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
+	p.library = j
+	p.jarToExport = p.memberType.jarToExportGetter(j)
+}
 
-	for _, dir := range j.AidlIncludeDirs() {
-		// TODO(jiyong): copy parcelable declarations only
-		aidlFiles, _ := sdkModuleContext.GlobWithDeps(dir.String()+"/**/*.aidl", nil)
-		for _, file := range aidlFiles {
-			builder.CopyToSnapshot(android.PathForSource(sdkModuleContext, file), filepath.Join(aidlIncludeDir, file))
+func (p *librarySdkMemberProperties) AddToPropertySet(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, propertySet android.BpPropertySet) {
+	if p.jarToExport != nil {
+		exportedJar := p.jarToExport
+		snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.OsPrefix(), p.library.Name())
+		builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
+
+		for _, dir := range p.library.AidlIncludeDirs() {
+			// TODO(jiyong): copy parcelable declarations only
+			aidlFiles, _ := sdkModuleContext.GlobWithDeps(dir.String()+"/**/*.aidl", nil)
+			for _, file := range aidlFiles {
+				builder.CopyToSnapshot(android.PathForSource(sdkModuleContext, file), filepath.Join(aidlIncludeDir, file))
+			}
 		}
+
+		propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
 	}
-
-	module := builder.AddPrebuiltModule(member, "java_import")
-	module.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
 }
 
-var javaHeaderLibsSdkMemberType android.SdkMemberType = &headerLibrarySdkMemberType{
-	librarySdkMemberType{
-		android.SdkMemberTypeBase{
-			PropertyName: "java_header_libs",
-			SupportsSdk:  true,
-		},
+var javaHeaderLibsSdkMemberType android.SdkMemberType = &librarySdkMemberType{
+	android.SdkMemberTypeBase{
+		PropertyName: "java_header_libs",
+		SupportsSdk:  true,
 	},
-}
-
-type headerLibrarySdkMemberType struct {
-	librarySdkMemberType
-}
-
-func (mt *headerLibrarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
-	mt.librarySdkMemberType.buildSnapshot(sdkModuleContext, builder, member, func(j *Library) android.Path {
+	func(j *Library) android.Path {
 		headerJars := j.HeaderJars()
 		if len(headerJars) != 1 {
 			panic(fmt.Errorf("there must be only one header jar from %q", j.Name()))
 		}
 
 		return headerJars[0]
-	})
-}
-
-type implLibrarySdkMemberType struct {
-	librarySdkMemberType
-}
-
-func (mt *implLibrarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
-	mt.librarySdkMemberType.buildSnapshot(sdkModuleContext, builder, member, func(j *Library) android.Path {
-		implementationJars := j.ImplementationJars()
-		if len(implementationJars) != 1 {
-			panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
-		}
-
-		return implementationJars[0]
-	})
+	},
 }
 
 // java_library builds and links sources into a `.jar` file for the device, and possibly for the host as well.
@@ -2112,31 +2108,44 @@
 	return ok
 }
 
-func (mt *testSdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
-	variants := member.Variants()
-	if len(variants) != 1 {
-		sdkModuleContext.ModuleErrorf("sdk contains %d variants of member %q but only one is allowed", len(variants), member.Name())
-		for _, variant := range variants {
-			sdkModuleContext.ModuleErrorf("    %q", variant)
-		}
-	}
-	variant := variants[0]
-	j := variant.(*Test)
+func (mt *testSdkMemberType) AddPrebuiltModule(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) android.BpModule {
+	return builder.AddPrebuiltModule(member, "java_test_import")
+}
 
-	implementationJars := j.ImplementationJars()
+func (mt *testSdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+	return &testSdkMemberProperties{}
+}
+
+type testSdkMemberProperties struct {
+	android.SdkMemberPropertiesBase
+
+	test        *Test
+	jarToExport android.Path
+}
+
+func (p *testSdkMemberProperties) PopulateFromVariant(variant android.SdkAware) {
+	test := variant.(*Test)
+
+	implementationJars := test.ImplementationJars()
 	if len(implementationJars) != 1 {
-		panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
+		panic(fmt.Errorf("there must be only one implementation jar from %q", test.Name()))
 	}
 
-	snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(member)
-	builder.CopyToSnapshot(implementationJars[0], snapshotRelativeJavaLibPath)
+	p.test = test
+	p.jarToExport = implementationJars[0]
+}
 
-	snapshotRelativeTestConfigPath := sdkSnapshotFilePathForMember(member, testConfigSuffix)
-	builder.CopyToSnapshot(j.testConfig, snapshotRelativeTestConfigPath)
+func (p *testSdkMemberProperties) AddToPropertySet(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, propertySet android.BpPropertySet) {
+	if p.jarToExport != nil {
+		snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.OsPrefix(), p.test.Name())
+		builder.CopyToSnapshot(p.jarToExport, snapshotRelativeJavaLibPath)
 
-	module := builder.AddPrebuiltModule(member, "java_test_import")
-	module.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
-	module.AddProperty("test_config", snapshotRelativeTestConfigPath)
+		snapshotRelativeTestConfigPath := sdkSnapshotFilePathForMember(p.OsPrefix(), p.test.Name(), testConfigSuffix)
+		builder.CopyToSnapshot(p.test.testConfig, snapshotRelativeTestConfigPath)
+
+		propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
+		propertySet.AddProperty("test_config", snapshotRelativeTestConfigPath)
+	}
 }
 
 // java_test builds a and links sources into a `.jar` file for the device, and possibly for the host as well, and
@@ -2339,7 +2348,7 @@
 //
 
 type ImportProperties struct {
-	Jars []string `android:"path"`
+	Jars []string `android:"path,arch_variant"`
 
 	Sdk_version *string
 
diff --git a/rust/rust.go b/rust/rust.go
index de6512c..f446ef0 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -325,6 +325,10 @@
 	panic("SetStubsVersions not yet implemented for rust modules")
 }
 
+func (mod *Module) StubsVersion() string {
+	panic("SetStubsVersions not yet implemented for rust modules")
+}
+
 func (mod *Module) BuildStaticVariant() bool {
 	if mod.compiler != nil {
 		if library, ok := mod.compiler.(libraryInterface); ok {
diff --git a/scripts/OWNERS b/scripts/OWNERS
index 076b3f5..9e97a60 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -1 +1,2 @@
 per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@google.com
+per-file build-mainline-modules.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
new file mode 100755
index 0000000..79e40dd
--- /dev/null
+++ b/scripts/build-mainline-modules.sh
@@ -0,0 +1,63 @@
+#!/bin/bash -ex
+
+# Non exhaustive list of modules where we want prebuilts. More can be added as
+# needed.
+MAINLINE_MODULES=(
+  com.android.art.debug
+  com.android.art.release
+  com.android.art.testing
+  com.android.conscrypt
+  com.android.runtime
+  com.android.tzdata
+  com.android.i18n
+)
+
+# List of SDKs and module exports we know of.
+MODULES_SDK_AND_EXPORTS=(
+  art-module-sdk
+  art-module-test-exports
+  conscrypt-module-sdk
+  conscrypt-module-test-exports
+)
+
+# We want to create apex modules for all supported architectures.
+PRODUCTS=(
+    aosp_arm
+    aosp_arm64
+    aosp_x86
+    aosp_x86_64
+)
+
+if [ ! -e "build/make/core/Makefile" ]; then
+    echo "$0 must be run from the top of the tree"
+    exit 1
+fi
+
+OUT_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var OUT_DIR)
+DIST_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var DIST_DIR)
+
+for product in "${PRODUCTS[@]}"; do
+    build/soong/soong_ui.bash --make-mode $@ \
+        TARGET_PRODUCT=${product} \
+        ${MAINLINE_MODULES[@]}
+
+    PRODUCT_OUT=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var PRODUCT_OUT)
+    TARGET_ARCH=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var TARGET_ARCH)
+    rm -rf ${DIST_DIR}/${TARGET_ARCH}/
+    mkdir -p ${DIST_DIR}/${TARGET_ARCH}/
+    for module in "${MAINLINE_MODULES[@]}"; do
+      cp ${PWD}/${PRODUCT_OUT}/system/apex/${module}.apex ${DIST_DIR}/${TARGET_ARCH}/
+    done
+done
+
+
+# Create multi-archs SDKs in a different out directory. The multi-arch script
+# uses soong directly and therefore needs its own directory that doesn't clash
+# with make.
+export OUT_DIR=${OUT_DIR}/aml/
+for sdk in "${MODULES_SDK_AND_EXPORTS[@]}"; do
+    build/soong/scripts/build-aml-prebuilts.sh ${sdk}
+done
+
+rm -rf ${DIST_DIR}/mainline-sdks
+cp -R ${OUT_DIR}/soong/mainline-sdks ${DIST_DIR}
diff --git a/scripts/build_broken_logs.go b/scripts/build_broken_logs.go
index 8021e55..82ba749 100644
--- a/scripts/build_broken_logs.go
+++ b/scripts/build_broken_logs.go
@@ -54,11 +54,13 @@
 	DefaultDeprecated
 )
 
-var buildBrokenSettings = []struct {
+type Setting struct {
 	name     string
 	behavior BuildBrokenBehavior
 	warnings []string
-}{
+}
+
+var buildBrokenSettings = []Setting{
 	{
 		name:     "BUILD_BROKEN_DUP_RULES",
 		behavior: DefaultFalse,
@@ -68,6 +70,19 @@
 		name:     "BUILD_BROKEN_USES_NETWORK",
 		behavior: DefaultDeprecated,
 	},
+	{
+		name:     "BUILD_BROKEN_USES_BUILD_COPY_HEADERS",
+		behavior: DefaultTrue,
+		warnings: []string{
+			"COPY_HEADERS has been deprecated",
+			"COPY_HEADERS is deprecated",
+		},
+	},
+}
+
+type Branch struct {
+	Settings []Setting
+	Logs     []ProductLog
 }
 
 type ProductBranch struct {
@@ -82,35 +97,48 @@
 }
 
 type Log struct {
-	BuildBroken []*bool
-	HasBroken   []bool
+	WarningModuleTypes []string
+	ErrorModuleTypes   []string
+
+	BuildBroken map[string]*bool
+	HasBroken   map[string]int
 }
 
 func Merge(l, l2 Log) Log {
-	if len(l.BuildBroken) == 0 {
-		l.BuildBroken = make([]*bool, len(buildBrokenSettings))
+	if l.BuildBroken == nil {
+		l.BuildBroken = map[string]*bool{}
 	}
-	if len(l.HasBroken) == 0 {
-		l.HasBroken = make([]bool, len(buildBrokenSettings))
+	if l.HasBroken == nil {
+		l.HasBroken = map[string]int{}
 	}
 
-	if len(l.BuildBroken) != len(l2.BuildBroken) || len(l.HasBroken) != len(l2.HasBroken) {
-		panic("mis-matched logs")
-	}
-
-	for i, v := range l.BuildBroken {
+	for n, v := range l.BuildBroken {
 		if v == nil {
-			l.BuildBroken[i] = l2.BuildBroken[i]
+			l.BuildBroken[n] = l2.BuildBroken[n]
 		}
 	}
-	for i := range l.HasBroken {
-		l.HasBroken[i] = l.HasBroken[i] || l2.HasBroken[i]
+	for n, v := range l2.BuildBroken {
+		if _, ok := l.BuildBroken[n]; !ok {
+			l.BuildBroken[n] = v
+		}
+	}
+
+	for n := range l.HasBroken {
+		if l.HasBroken[n] < l2.HasBroken[n] {
+			l.HasBroken[n] = l2.HasBroken[n]
+		}
+	}
+	for n := range l2.HasBroken {
+		if _, ok := l.HasBroken[n]; !ok {
+			l.HasBroken[n] = l2.HasBroken[n]
+		}
 	}
 
 	return l
 }
 
-func PrintResults(products []ProductLog) {
+func PrintResults(branch Branch) {
+	products := branch.Logs
 	devices := map[string]Log{}
 	deviceNames := []string{}
 
@@ -124,39 +152,48 @@
 
 	sort.Strings(deviceNames)
 
-	for i, setting := range buildBrokenSettings {
+	for _, setting := range branch.Settings {
 		printed := false
+		n := setting.name
 
 		for _, device := range deviceNames {
 			log := devices[device]
 
 			if setting.behavior == DefaultTrue {
-				if log.BuildBroken[i] == nil || *log.BuildBroken[i] == false {
-					if log.HasBroken[i] {
+				if log.BuildBroken[n] == nil || *log.BuildBroken[n] == false {
+					if log.HasBroken[n] > 0 {
 						printed = true
-						fmt.Printf("  %s needs to set %s := true\n", device, setting.name)
+						plural := ""
+						if log.HasBroken[n] > 1 {
+							plural = "s"
+						}
+						fmt.Printf("  %s needs to set %s := true  (%d instance%s)\n", device, setting.name, log.HasBroken[n], plural)
 					}
-				} else if !log.HasBroken[i] {
+				} else if log.HasBroken[n] == 0 {
 					printed = true
 					fmt.Printf("  %s sets %s := true, but does not need it\n", device, setting.name)
 				}
 			} else if setting.behavior == DefaultFalse {
-				if log.BuildBroken[i] == nil {
+				if log.BuildBroken[n] == nil {
 					// Nothing to be done
-				} else if *log.BuildBroken[i] == false {
+				} else if *log.BuildBroken[n] == false {
 					printed = true
 					fmt.Printf("  %s sets %s := false, which is the default and can be removed\n", device, setting.name)
-				} else if !log.HasBroken[i] {
+				} else if log.HasBroken[n] == 0 {
 					printed = true
 					fmt.Printf("  %s sets %s := true, but does not need it\n", device, setting.name)
 				}
 			} else if setting.behavior == DefaultDeprecated {
-				if log.BuildBroken[i] != nil {
+				if log.BuildBroken[n] != nil {
 					printed = true
-					if log.HasBroken[i] {
-						fmt.Printf("  %s sets %s := %v, which is deprecated, but has failures\n", device, setting.name, *log.BuildBroken[i])
+					if log.HasBroken[n] > 0 {
+						plural := ""
+						if log.HasBroken[n] > 1 {
+							plural = "s"
+						}
+						fmt.Printf("  %s sets %s := %v, which is deprecated, but has %d failure%s\n", device, setting.name, *log.BuildBroken[n], log.HasBroken[n], plural)
 					} else {
-						fmt.Printf("  %s sets %s := %v, which is deprecated and can be removed\n", device, setting.name, *log.BuildBroken[i])
+						fmt.Printf("  %s sets %s := %v, which is deprecated and can be removed\n", device, setting.name, *log.BuildBroken[n])
 					}
 				}
 			}
@@ -168,17 +205,45 @@
 	}
 }
 
-func ParseBranch(name string) []ProductLog {
+func ParseBranch(name string) Branch {
 	products, err := filepath.Glob(filepath.Join(name, "*"))
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	ret := []ProductLog{}
+	ret := Branch{Logs: []ProductLog{}}
 	for _, product := range products {
 		product = filepath.Base(product)
 
-		ret = append(ret, ParseProduct(ProductBranch{Branch: name, Name: product}))
+		ret.Logs = append(ret.Logs, ParseProduct(ProductBranch{Branch: name, Name: product}))
+	}
+
+	ret.Settings = append(ret.Settings, buildBrokenSettings...)
+	if len(ret.Logs) > 0 {
+		for _, mtype := range ret.Logs[0].WarningModuleTypes {
+			if mtype == "BUILD_COPY_HEADERS" || mtype == "" {
+				continue
+			}
+			ret.Settings = append(ret.Settings, Setting{
+				name:     "BUILD_BROKEN_USES_" + mtype,
+				behavior: DefaultTrue,
+				warnings: []string{mtype + " has been deprecated"},
+			})
+		}
+		for _, mtype := range ret.Logs[0].ErrorModuleTypes {
+			if mtype == "BUILD_COPY_HEADERS" || mtype == "" {
+				continue
+			}
+			ret.Settings = append(ret.Settings, Setting{
+				name:     "BUILD_BROKEN_USES_" + mtype,
+				behavior: DefaultFalse,
+				warnings: []string{mtype + " has been deprecated"},
+			})
+		}
+	}
+
+	for _, productLog := range ret.Logs {
+		ScanProduct(ret.Settings, productLog)
 	}
 	return ret
 }
@@ -192,15 +257,15 @@
 	ret := ProductLog{
 		ProductBranch: p,
 		Log: Log{
-			BuildBroken: make([]*bool, len(buildBrokenSettings)),
-			HasBroken:   make([]bool, len(buildBrokenSettings)),
+			BuildBroken: map[string]*bool{},
+			HasBroken:   map[string]int{},
 		},
 	}
 
 	lines := strings.Split(string(soongLog), "\n")
 	for _, line := range lines {
 		fields := strings.Split(line, " ")
-		if len(fields) != 5 {
+		if len(fields) < 5 {
 			continue
 		}
 
@@ -208,30 +273,35 @@
 			ret.Device = fields[4]
 		}
 
+		if fields[3] == "DEFAULT_WARNING_BUILD_MODULE_TYPES" {
+			ret.WarningModuleTypes = fields[4:]
+		}
+		if fields[3] == "DEFAULT_ERROR_BUILD_MODULE_TYPES" {
+			ret.ErrorModuleTypes = fields[4:]
+		}
+
 		if strings.HasPrefix(fields[3], "BUILD_BROKEN_") {
-			for i, setting := range buildBrokenSettings {
-				if setting.name == fields[3] {
-					ret.BuildBroken[i] = ParseBoolPtr(fields[4])
-				}
-			}
+			ret.BuildBroken[fields[3]] = ParseBoolPtr(fields[4])
 		}
 	}
 
-	stdLog, err := ioutil.ReadFile(filepath.Join(p.Branch, p.Name, "std_full.log"))
+	return ret
+}
+
+func ScanProduct(settings []Setting, l ProductLog) {
+	stdLog, err := ioutil.ReadFile(filepath.Join(l.Branch, l.Name, "std_full.log"))
 	if err != nil {
 		log.Fatal(err)
 	}
 	stdStr := string(stdLog)
 
-	for i, setting := range buildBrokenSettings {
+	for _, setting := range settings {
 		for _, warning := range setting.warnings {
 			if strings.Contains(stdStr, warning) {
-				ret.HasBroken[i] = true
+				l.HasBroken[setting.name] += strings.Count(stdStr, warning)
 			}
 		}
 	}
-
-	return ret
 }
 
 func ParseBoolPtr(str string) *bool {
diff --git a/scripts/generate-notice-files.py b/scripts/generate-notice-files.py
new file mode 100755
index 0000000..49011b2
--- /dev/null
+++ b/scripts/generate-notice-files.py
@@ -0,0 +1,267 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012 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.
+"""
+Usage: generate-notice-files --text-output [plain text output file] \
+               --html-output [html output file] \
+               --xml-output [xml output file] \
+               -t [file title] -s [directory of notices]
+
+Generate the Android notice files, including both text and html files.
+
+-h to display this usage message and exit.
+"""
+from collections import defaultdict
+import argparse
+import hashlib
+import itertools
+import os
+import os.path
+import re
+import sys
+
+MD5_BLOCKSIZE = 1024 * 1024
+HTML_ESCAPE_TABLE = {
+    "&": "&amp;",
+    '"': "&quot;",
+    "'": "&apos;",
+    ">": "&gt;",
+    "<": "&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.
+    Output should be compatible with md5sum command"""
+
+    f = open(filename, "rb")
+    sum = hashlib.md5()
+    while 1:
+        block = f.read(MD5_BLOCKSIZE)
+        if not block:
+            break
+        sum.update(block)
+    f.close()
+    return hexify(sum.digest())
+
+
+def html_escape(text):
+    """Produce entities within text."""
+    return "".join(HTML_ESCAPE_TABLE.get(c,c) for c in text)
+
+HTML_OUTPUT_CSS="""
+<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):
+    """Combine notice files in FILE_HASH and output a HTML version to OUTPUT_FILENAME."""
+
+    SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
+
+    # 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 = {}
+    id_count = 0
+    for value in file_hash:
+        for filename in value:
+             id_table[filename] = id_count
+        id_count += 1
+
+    # 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 our table of contents
+    print >> output_file, '<div class="toc">'
+    print >> output_file, "<ul>"
+
+    # Flatten the list of lists into a single list of filenames
+    sorted_filenames = sorted(itertools.chain.from_iterable(file_hash))
+
+    # 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)
+
+    print >> output_file, "</ul>"
+    print >> output_file, "</div><!-- table of contents -->"
+    # Output the individual notice file lists
+    print >>output_file, '<table cellpadding="0" cellspacing="0" border="0">'
+    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">'
+        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
+
+    # Finish off the file output
+    print >> output_file, "</table>"
+    print >> output_file, "</body></html>"
+    output_file.close()
+
+def combine_notice_files_text(file_hash, input_dir, output_filename, file_title):
+    """Combine notice files in FILE_HASH and output a text version to OUTPUT_FILENAME."""
+
+    SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
+    output_file = open(output_filename, "wb")
+    print >> output_file, file_title
+    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.close()
+
+def combine_notice_files_xml(files_with_same_hash, input_dir, output_filename):
+    """Combine notice files in FILE_HASH and output a XML version to OUTPUT_FILENAME."""
+
+    SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
+
+    # 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]:
+             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>"
+
+    # Flatten the list of lists into a single list of filenames
+    sorted_filenames = sorted(id_table.keys())
+
+    # 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
+
+    processed_file_keys = []
+    # Output the individual notice file lists
+    for filename in sorted_filenames:
+        file_key = id_table.get(filename)
+        if file_key in processed_file_keys:
+            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
+
+    # Finish off the file output
+    print >> output_file, "</licenses>"
+    output_file.close()
+
+def get_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        '--text-output', required=True,
+        help='The text output file path.')
+    parser.add_argument(
+        '--html-output',
+        help='The html output file path.')
+    parser.add_argument(
+        '--xml-output',
+        help='The xml output file path.')
+    parser.add_argument(
+        '-t', '--title', required=True,
+        help='The file title.')
+    parser.add_argument(
+        '-s', '--source-dir', required=True,
+        help='The directory containing notices.')
+    parser.add_argument(
+        '-i', '--included-subdirs', action='append',
+        help='The sub directories which should be included.')
+    parser.add_argument(
+        '-e', '--excluded-subdirs', action='append',
+        help='The sub directories which should be excluded.')
+    return parser.parse_args()
+
+def main(argv):
+    args = get_args()
+
+    txt_output_file = args.text_output
+    html_output_file = args.html_output
+    xml_output_file = args.xml_output
+    file_title = args.title
+    included_subdirs = []
+    excluded_subdirs = []
+    if args.included_subdirs is not None:
+        included_subdirs = args.included_subdirs
+    if args.excluded_subdirs is not None:
+        excluded_subdirs = args.excluded_subdirs
+
+    # Find all the notice files and md5 them
+    input_dir = os.path.normpath(args.source_dir)
+    files_with_same_hash = defaultdict(list)
+    for root, dir, files in os.walk(input_dir):
+        for file in files:
+            matched = True
+            if len(included_subdirs) > 0:
+                matched = False
+                for subdir in included_subdirs:
+                    if (root == (input_dir + '/' + subdir) or
+                        root.startswith(input_dir + '/' + subdir + '/')):
+                        matched = True
+                        break
+            elif len(excluded_subdirs) > 0:
+                for subdir in excluded_subdirs:
+                    if (root == (input_dir + '/' + subdir) or
+                        root.startswith(input_dir + '/' + subdir + '/')):
+                        matched = False
+                        break
+            if matched and file.endswith(".txt"):
+                filename = os.path.join(root, file)
+                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())]
+
+    combine_notice_files_text(filesets, input_dir, txt_output_file, file_title)
+
+    if html_output_file is not None:
+        combine_notice_files_html(filesets, input_dir, html_output_file)
+
+    if xml_output_file is not None:
+        combine_notice_files_xml(files_with_same_hash, input_dir, xml_output_file)
+
+if __name__ == "__main__":
+    main(sys.argv)
diff --git a/scripts/mergenotice.py b/scripts/mergenotice.py
index 407ae8c..fe99073 100755
--- a/scripts/mergenotice.py
+++ b/scripts/mergenotice.py
@@ -16,7 +16,7 @@
 #
 """
 Merges input notice files to the output file while ignoring duplicated files
-This script shouldn't be confused with build/make/tools/generate-notice-files.py
+This script shouldn't be confused with build/soong/scripts/generate-notice-files.py
 which is responsible for creating the final notice file for all artifacts
 installed. This script has rather limited scope; it is meant to create a merged
 notice file for a set of modules that are packaged together, e.g. in an APEX.
diff --git a/sdk/bp.go b/sdk/bp.go
index c2a75e4..68fe7ab 100644
--- a/sdk/bp.go
+++ b/sdk/bp.go
@@ -35,7 +35,7 @@
 
 func (s *bpPropertySet) AddProperty(name string, value interface{}) {
 	if s.properties[name] != nil {
-		panic("Property %q already exists in property set")
+		panic(fmt.Sprintf("Property %q already exists in property set", name))
 	}
 
 	s.properties[name] = value
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index ecb1da0..11bc902 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -17,6 +17,7 @@
 import (
 	"testing"
 
+	"android/soong/android"
 	"android/soong/cc"
 )
 
@@ -24,11 +25,13 @@
 	t.Helper()
 
 	fs := map[string][]byte{
-		"Test.cpp":                  nil,
-		"include/Test.h":            nil,
-		"arm64/include/Arm64Test.h": nil,
-		"libfoo.so":                 nil,
-		"aidl/foo/bar/Test.aidl":    nil,
+		"Test.cpp":                      nil,
+		"include/Test.h":                nil,
+		"include-android/AndroidTest.h": nil,
+		"include-host/HostTest.h":       nil,
+		"arm64/include/Arm64Test.h":     nil,
+		"libfoo.so":                     nil,
+		"aidl/foo/bar/Test.aidl":        nil,
 	}
 	return testSdkWithFs(t, bp, fs)
 }
@@ -54,7 +57,7 @@
 	arm64Output := result.Module("sdkmember", "android_arm64_armv8-a_shared").(*cc.Module).OutputFile()
 
 	var inputs []string
-	buildParams := result.Module("mysdk", "android_common").BuildParamsForTests()
+	buildParams := result.Module("mysdk", android.CommonOS.Name).BuildParamsForTests()
 	for _, bp := range buildParams {
 		if bp.Input != nil {
 			inputs = append(inputs, bp.Input.String())
@@ -250,7 +253,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAllCopyRules(`
 include/Test.h -> include/include/Test.h
 .intermediates/mynativelib1/android_arm64_armv8-a_shared/mynativelib1.so -> arm64/lib/mynativelib1.so
@@ -287,7 +290,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -356,7 +359,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mymodule_exports", "android_common", "",
+	result.CheckSnapshot("mymodule_exports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -400,6 +403,108 @@
 	)
 }
 
+func TestMultipleHostOsTypesSnapshotWithCcBinary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		module_exports {
+			name: "myexports",
+			device_supported: false,
+			host_supported: true,
+			native_binaries: ["mynativebinary"],
+			target: {
+				windows: {
+					enabled: true,
+				},
+			},
+		}
+
+		cc_binary {
+			name: "mynativebinary",
+			device_supported: false,
+			host_supported: true,
+			srcs: [
+				"Test.cpp",
+			],
+			compile_multilib: "both",
+			system_shared_libs: [],
+			stl: "none",
+			target: {
+				windows: {
+					enabled: true,
+				},
+			},
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_binary {
+    name: "myexports_mynativebinary@current",
+    sdk_member_name: "mynativebinary",
+    device_supported: false,
+    host_supported: true,
+    target: {
+        linux_glibc: {
+            compile_multilib: "both",
+        },
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/bin/mynativebinary"],
+        },
+        linux_glibc_x86: {
+            srcs: ["linux_glibc/x86/bin/mynativebinary"],
+        },
+        windows: {
+            compile_multilib: "64",
+        },
+        windows_x86_64: {
+            srcs: ["windows/x86_64/bin/mynativebinary.exe"],
+        },
+    },
+}
+
+cc_prebuilt_binary {
+    name: "mynativebinary",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    target: {
+        linux_glibc: {
+            compile_multilib: "both",
+        },
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/bin/mynativebinary"],
+        },
+        linux_glibc_x86: {
+            srcs: ["linux_glibc/x86/bin/mynativebinary"],
+        },
+        windows: {
+            compile_multilib: "64",
+        },
+        windows_x86_64: {
+            srcs: ["windows/x86_64/bin/mynativebinary.exe"],
+        },
+    },
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    device_supported: false,
+    host_supported: true,
+    native_binaries: ["myexports_mynativebinary@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/mynativebinary/linux_glibc_x86_64/mynativebinary -> linux_glibc/x86_64/bin/mynativebinary
+.intermediates/mynativebinary/linux_glibc_x86/mynativebinary -> linux_glibc/x86/bin/mynativebinary
+.intermediates/mynativebinary/windows_x86_64/mynativebinary.exe -> windows/x86_64/bin/mynativebinary.exe
+`),
+	)
+}
+
 func TestSnapshotWithCcSharedLibrary(t *testing.T) {
 	result := testSdkWithCc(t, `
 		sdk {
@@ -413,6 +518,7 @@
 				"Test.cpp",
 				"aidl/foo/bar/Test.aidl",
 			],
+			apex_available: ["apex1", "apex2"],
 			export_include_dirs: ["include"],
 			aidl: {
 				export_aidl_headers: true,
@@ -422,13 +528,17 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
     name: "mysdk_mynativelib@current",
     sdk_member_name: "mynativelib",
+    apex_available: [
+        "apex1",
+        "apex2",
+    ],
     export_include_dirs: ["include/include"],
     arch: {
         arm64: {
@@ -447,6 +557,10 @@
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
+    apex_available: [
+        "apex1",
+        "apex2",
+    ],
     export_include_dirs: ["include/include"],
     arch: {
         arm64: {
@@ -507,10 +621,11 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			sdk_version: "minimum",
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -519,6 +634,7 @@
     sdk_member_name: "mynativelib",
     device_supported: false,
     host_supported: true,
+    sdk_version: "minimum",
     export_include_dirs: ["include/include"],
     arch: {
         x86_64: {
@@ -539,6 +655,7 @@
     prefer: false,
     device_supported: false,
     host_supported: true,
+    sdk_version: "minimum",
     export_include_dirs: ["include/include"],
     arch: {
         x86_64: {
@@ -575,6 +692,99 @@
 	)
 }
 
+func TestMultipleHostOsTypesSnapshotWithCcSharedLibrary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			device_supported: false,
+			host_supported: true,
+			native_shared_libs: ["mynativelib"],
+			target: {
+				windows: {
+					enabled: true,
+				},
+			},
+		}
+
+		cc_library_shared {
+			name: "mynativelib",
+			device_supported: false,
+			host_supported: true,
+			srcs: [
+				"Test.cpp",
+			],
+			system_shared_libs: [],
+			stl: "none",
+			target: {
+				windows: {
+					enabled: true,
+				},
+			},
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+    name: "mysdk_mynativelib@current",
+    sdk_member_name: "mynativelib",
+    device_supported: false,
+    host_supported: true,
+    target: {
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+        },
+        linux_glibc_x86: {
+            srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+        },
+        windows_x86_64: {
+            srcs: ["windows/x86_64/lib/mynativelib.dll"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+cc_prebuilt_library_shared {
+    name: "mynativelib",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    target: {
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+        },
+        linux_glibc_x86: {
+            srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+        },
+        windows_x86_64: {
+            srcs: ["windows/x86_64/lib/mynativelib.dll"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    device_supported: false,
+    host_supported: true,
+    native_shared_libs: ["mysdk_mynativelib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
+.intermediates/mynativelib/windows_x86_64_shared/mynativelib.dll -> windows/x86_64/lib/mynativelib.dll
+`),
+	)
+}
+
 func TestSnapshotWithCcStaticLibrary(t *testing.T) {
 	result := testSdkWithCc(t, `
 		module_exports {
@@ -597,7 +807,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "android_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -685,7 +895,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -784,7 +994,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -856,7 +1066,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -909,7 +1119,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -945,3 +1155,83 @@
 `),
 	)
 }
+
+func TestDeviceAndHostSnapshotWithCcHeadersLibrary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			host_supported: true,
+			native_header_libs: ["mynativeheaders"],
+		}
+
+		cc_library_headers {
+			name: "mynativeheaders",
+			host_supported: true,
+			system_shared_libs: [],
+			stl: "none",
+			export_system_include_dirs: ["include"],
+			target: {
+				android: {
+					export_include_dirs: ["include-android"],
+				},
+				host: {
+					export_include_dirs: ["include-host"],
+				},
+			},
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_headers {
+    name: "mysdk_mynativeheaders@current",
+    sdk_member_name: "mynativeheaders",
+    host_supported: true,
+    export_system_include_dirs: ["include/include"],
+    target: {
+        android: {
+            export_include_dirs: ["include/include-android"],
+        },
+        linux_glibc: {
+            export_include_dirs: ["include/include-host"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+cc_prebuilt_library_headers {
+    name: "mynativeheaders",
+    prefer: false,
+    host_supported: true,
+    export_system_include_dirs: ["include/include"],
+    target: {
+        android: {
+            export_include_dirs: ["include/include-android"],
+        },
+        linux_glibc: {
+            export_include_dirs: ["include/include-host"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    host_supported: true,
+    native_header_libs: ["mysdk_mynativeheaders@current"],
+}
+`),
+		checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+include-android/AndroidTest.h -> include/include-android/AndroidTest.h
+include-host/HostTest.h -> include/include-host/HostTest.h
+`),
+	)
+}
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
index b905d71..20e2521 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -42,7 +42,7 @@
 			"package/Android.bp": []byte(packageBp),
 		})
 
-	result.CheckSnapshot("myexports", "android_common", "package",
+	result.CheckSnapshot("myexports", "package",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 0737e5e..45da1f9 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -141,7 +141,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -196,7 +196,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -230,6 +230,72 @@
 	)
 }
 
+func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			host_supported: true,
+			java_header_libs: ["myjavalib"],
+		}
+
+		java_library {
+			name: "myjavalib",
+			host_supported: true,
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    host_supported: true,
+    target: {
+        android: {
+            jars: ["java/android/myjavalib.jar"],
+        },
+        linux_glibc: {
+            jars: ["java/linux_glibc/myjavalib.jar"],
+        },
+    },
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    host_supported: true,
+    target: {
+        android: {
+            jars: ["java/android/myjavalib.jar"],
+        },
+        linux_glibc: {
+            jars: ["java/linux_glibc/myjavalib.jar"],
+        },
+    },
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    host_supported: true,
+    java_header_libs: ["mysdk_myjavalib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/android/myjavalib.jar
+.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/linux_glibc/myjavalib.jar
+`),
+	)
+}
+
 func TestSnapshotWithJavaImplLibrary(t *testing.T) {
 	result := testSdkWithJava(t, `
 		module_exports {
@@ -250,7 +316,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "android_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -305,7 +371,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -356,7 +422,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "android_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -409,7 +475,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -503,7 +569,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "android_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -526,7 +592,7 @@
 
 `),
 		checkAllCopyRules(""),
-		checkMergeZip(".intermediates/myexports/android_common/tmp/java/myjavaapistubs_stubs_sources.zip"),
+		checkMergeZip(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"),
 	)
 }
 
@@ -552,7 +618,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -580,7 +646,7 @@
 }
 `),
 		checkAllCopyRules(""),
-		checkMergeZip(".intermediates/myexports/linux_glibc_common/tmp/java/myjavaapistubs_stubs_sources.zip"),
+		checkMergeZip(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"),
 	)
 }
 
@@ -612,7 +678,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -702,7 +768,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
diff --git a/sdk/sdk.go b/sdk/sdk.go
index c194ac1..db71575 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -53,6 +53,13 @@
 	// The set of exported members.
 	exportedMembers map[string]struct{}
 
+	// Information about the OsType specific member variants associated with this variant.
+	//
+	// Set by OsType specific variants when their GenerateAndroidBuildActions is invoked
+	// and used by the CommonOS variant when its GenerateAndroidBuildActions is invoked, which
+	// is guaranteed to occur afterwards.
+	memberRefs []sdkMemberRef
+
 	properties sdkProperties
 
 	snapshotFile android.OptionalPath
@@ -201,7 +208,7 @@
 	// properties for the member type specific list properties.
 	s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberListProperties()
 	s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties)
-	android.InitAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon)
+	android.InitCommonOSAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(s)
 	android.AddLoadHook(s, func(ctx android.LoadHookContext) {
 		type props struct {
@@ -252,10 +259,31 @@
 }
 
 func (s *sdk) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if !s.snapshot() {
+	if s.snapshot() {
 		// We don't need to create a snapshot out of sdk_snapshot.
 		// That doesn't make sense. We need a snapshot to create sdk_snapshot.
-		s.snapshotFile = android.OptionalPathForPath(s.buildSnapshot(ctx))
+		return
+	}
+
+	// This method is guaranteed to be called on OsType specific variants before it is called
+	// on their corresponding CommonOS variant.
+	if !s.IsCommonOSVariant() {
+		// Collect the OsType specific members are add them to the OsType specific variant.
+		s.memberRefs = s.collectMembers(ctx)
+	} else {
+		// Get the OsType specific variants on which the CommonOS depends.
+		osSpecificVariants := android.GetOsSpecificVariantsOfCommonOSVariant(ctx)
+		var sdkVariants []*sdk
+		for _, m := range osSpecificVariants {
+			if sdkVariant, ok := m.(*sdk); ok {
+				sdkVariants = append(sdkVariants, sdkVariant)
+			}
+		}
+
+		// Generate the snapshot from the member info.
+		p := s.buildSnapshot(ctx, sdkVariants)
+		s.snapshotFile = android.OptionalPathForPath(p)
+		ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), s.Name()+"-current.zip", p)
 	}
 }
 
@@ -320,7 +348,8 @@
 // Step 1: create dependencies from an SDK module to its members.
 func memberMutator(mctx android.BottomUpMutatorContext) {
 	if s, ok := mctx.Module().(*sdk); ok {
-		if s.Enabled() {
+		// Add dependencies from enabled and non CommonOS variants to the sdk member variants.
+		if s.Enabled() && !s.IsCommonOSVariant() {
 			for _, memberListProperty := range s.memberListProperties() {
 				names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
 				if len(names) > 0 {
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 934bdae..243b976 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -145,7 +145,7 @@
 			"package/Android.bp": []byte(packageBp),
 		})
 
-	result.CheckSnapshot("mysdk", "android_common", "package",
+	result.CheckSnapshot("mysdk", "package",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -205,3 +205,19 @@
 }
 `))
 }
+
+func TestSDkInstall(t *testing.T) {
+	sdk := `
+		sdk {
+			name: "mysdk",
+		}
+	`
+	result := testSdkWithFs(t, ``,
+		map[string][]byte{
+			"Android.bp": []byte(sdk),
+		})
+
+	result.CheckSnapshot("mysdk", "",
+		checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`),
+	)
+}
diff --git a/sdk/testing.go b/sdk/testing.go
index 7352c74..464c3ca 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -40,7 +40,7 @@
 			name: "myapex.cert",
 			certificate: "myapex",
 		}
-	` + cc.GatherRequiredDepsForTest(android.Android)
+	` + cc.GatherRequiredDepsForTest(android.Android, android.Windows)
 
 	mockFS := map[string][]byte{
 		"build/make/target/product/security":         nil,
@@ -69,6 +69,16 @@
 
 	ctx := android.NewTestArchContext()
 
+	// Enable androidmk support.
+	// * Register the singleton
+	// * Configure that we are inside make
+	// * Add CommonOS to ensure that androidmk processing works.
+	android.RegisterAndroidMkBuildComponents(ctx)
+	android.SetInMakeForTests(config)
+	config.Targets[android.CommonOS] = []android.Target{
+		{android.CommonOS, android.Arch{ArchType: android.Common}, android.NativeBridgeDisabled, "", ""},
+	}
+
 	// from android package
 	android.RegisterPackageBuildComponents(ctx)
 	ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
@@ -188,15 +198,23 @@
 
 	buildParams := sdk.BuildParamsForTests()
 	copyRules := &strings.Builder{}
+	otherCopyRules := &strings.Builder{}
+	snapshotDirPrefix := sdk.builderForTests.snapshotDir.String() + "/"
 	for _, bp := range buildParams {
 		switch bp.Rule.String() {
 		case android.Cp.String():
-			// Get source relative to build directory.
-			src := android.NormalizePathForTesting(bp.Input)
+			output := bp.Output
 			// Get destination relative to the snapshot root
-			dest := bp.Output.Rel()
-			_, _ = fmt.Fprintf(copyRules, "%s -> %s\n", src, dest)
-			info.snapshotContents = append(info.snapshotContents, dest)
+			dest := output.Rel()
+			src := android.NormalizePathForTesting(bp.Input)
+			// We differentiate between copy rules for the snapshot, and copy rules for the install file.
+			if strings.HasPrefix(output.String(), snapshotDirPrefix) {
+				// Get source relative to build directory.
+				_, _ = fmt.Fprintf(copyRules, "%s -> %s\n", src, dest)
+				info.snapshotContents = append(info.snapshotContents, dest)
+			} else {
+				_, _ = fmt.Fprintf(otherCopyRules, "%s -> %s\n", src, dest)
+			}
 
 		case repackageZip.String():
 			// Add the destdir to the snapshot contents as that is effectively where
@@ -229,6 +247,7 @@
 	}
 
 	info.copyRules = copyRules.String()
+	info.otherCopyRules = otherCopyRules.String()
 
 	return info
 }
@@ -246,9 +265,12 @@
 // Takes a list of functions which check different facets of the snapshot build rules.
 // Allows each test to customize what is checked without duplicating lots of code
 // or proliferating check methods of different flavors.
-func (r *testSdkResult) CheckSnapshot(name string, variant string, dir string, checkers ...snapshotBuildInfoChecker) {
+func (r *testSdkResult) CheckSnapshot(name string, dir string, checkers ...snapshotBuildInfoChecker) {
 	r.t.Helper()
 
+	// The sdk CommonOS variant is always responsible for generating the snapshot.
+	variant := android.CommonOS.Name
+
 	sdk := r.Module(name, variant).(*sdk)
 
 	snapshotBuildInfo := r.getSdkSnapshotBuildInfo(sdk)
@@ -301,6 +323,13 @@
 	}
 }
 
+func checkAllOtherCopyRules(expected string) snapshotBuildInfoChecker {
+	return func(info *snapshotBuildInfo) {
+		info.r.t.Helper()
+		info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.otherCopyRules)
+	}
+}
+
 // Check that the specified path is in the list of zips to merge with the intermediate zip.
 func checkMergeZip(expected string) snapshotBuildInfoChecker {
 	return func(info *snapshotBuildInfo) {
@@ -327,10 +356,14 @@
 	// snapshot.
 	snapshotContents []string
 
-	// A formatted representation of the src/dest pairs, one pair per line, of the format
-	// src -> dest
+	// A formatted representation of the src/dest pairs for a snapshot, one pair per line,
+	// of the format src -> dest
 	copyRules string
 
+	// A formatted representation of the src/dest pairs for files not in a snapshot, one pair
+	// per line, of the format src -> dest
+	otherCopyRules string
+
 	// The path to the intermediate zip, which is a zip created from the source files copied
 	// into the snapshot directory and which will be merged with other zips to form the final output.
 	// Is am empty string if there is no intermediate zip because there are no zips to merge in.
diff --git a/sdk/update.go b/sdk/update.go
index d211e80..282a7a4 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"reflect"
+	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -104,18 +105,9 @@
 
 // Collect all the members.
 //
-// The members are first grouped by type and then grouped by name. The order of
-// the types is the order they are referenced in android.SdkMemberTypesRegistry.
-// The names are in the order in which the dependencies were added.
-//
-// Returns the members as well as the multilib setting to use.
-func (s *sdk) collectMembers(ctx android.ModuleContext) ([]*sdkMember, string) {
-	byType := make(map[android.SdkMemberType][]*sdkMember)
-	byName := make(map[string]*sdkMember)
-
-	lib32 := false // True if any of the members have 32 bit version.
-	lib64 := false // True if any of the members have 64 bit version.
-
+// Returns a list containing type (extracted from the dependency tag) and the variant.
+func (s *sdk) collectMembers(ctx android.ModuleContext) []sdkMemberRef {
+	var memberRefs []sdkMemberRef
 	ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
 		tag := ctx.OtherModuleDependencyTag(child)
 		if memberTag, ok := tag.(android.SdkMemberTypeDependencyTag); ok {
@@ -126,24 +118,7 @@
 				ctx.ModuleErrorf("module %q is not valid in property %s", ctx.OtherModuleName(child), memberType.SdkPropertyName())
 			}
 
-			name := ctx.OtherModuleName(child)
-			member := byName[name]
-			if member == nil {
-				member = &sdkMember{memberType: memberType, name: name}
-				byName[name] = member
-				byType[memberType] = append(byType[memberType], member)
-			}
-
-			multilib := child.Target().Arch.ArchType.Multilib
-			if multilib == "lib32" {
-				lib32 = true
-			} else if multilib == "lib64" {
-				lib64 = true
-			}
-
-			// Only append new variants to the list. This is needed because a member can be both
-			// exported by the sdk and also be a transitive sdk member.
-			member.variants = appendUniqueVariants(member.variants, child.(android.SdkAware))
+			memberRefs = append(memberRefs, sdkMemberRef{memberType, child.(android.SdkAware)})
 
 			// If the member type supports transitive sdk members then recurse down into
 			// its dependencies, otherwise exit traversal.
@@ -153,6 +128,47 @@
 		return false
 	})
 
+	return memberRefs
+}
+
+// Organize the members.
+//
+// The members are first grouped by type and then grouped by name. The order of
+// the types is the order they are referenced in android.SdkMemberTypesRegistry.
+// The names are in the order in which the dependencies were added.
+//
+// Returns the members as well as the multilib setting to use.
+func (s *sdk) organizeMembers(ctx android.ModuleContext, memberRefs []sdkMemberRef) ([]*sdkMember, string) {
+	byType := make(map[android.SdkMemberType][]*sdkMember)
+	byName := make(map[string]*sdkMember)
+
+	lib32 := false // True if any of the members have 32 bit version.
+	lib64 := false // True if any of the members have 64 bit version.
+
+	for _, memberRef := range memberRefs {
+		memberType := memberRef.memberType
+		variant := memberRef.variant
+
+		name := ctx.OtherModuleName(variant)
+		member := byName[name]
+		if member == nil {
+			member = &sdkMember{memberType: memberType, name: name}
+			byName[name] = member
+			byType[memberType] = append(byType[memberType], member)
+		}
+
+		multilib := variant.Target().Arch.ArchType.Multilib
+		if multilib == "lib32" {
+			lib32 = true
+		} else if multilib == "lib64" {
+			lib64 = true
+		}
+
+		// Only append new variants to the list. This is needed because a member can be both
+		// exported by the sdk and also be a transitive sdk member.
+		member.variants = appendUniqueVariants(member.variants, variant)
+	}
+
 	var members []*sdkMember
 	for _, memberListProperty := range s.memberListProperties() {
 		membersOfType := byType[memberListProperty.memberType]
@@ -207,7 +223,13 @@
 
 // buildSnapshot is the main function in this source file. It creates rules to copy
 // the contents (header files, stub libraries, etc) into the zip file.
-func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {
+func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) android.OutputPath {
+
+	var memberRefs []sdkMemberRef
+	for _, sdkVariant := range sdkVariants {
+		memberRefs = append(memberRefs, sdkVariant.memberRefs...)
+	}
+
 	snapshotDir := android.PathForModuleOut(ctx, "snapshot")
 
 	bp := newGeneratedFile(ctx, "snapshot", "Android.bp")
@@ -228,9 +250,16 @@
 	}
 	s.builderForTests = builder
 
-	members, multilib := s.collectMembers(ctx)
+	members, multilib := s.organizeMembers(ctx, memberRefs)
 	for _, member := range members {
-		member.memberType.BuildSnapshot(ctx, builder, member)
+		memberType := member.memberType
+		prebuiltModule := memberType.AddPrebuiltModule(ctx, builder, member)
+		if prebuiltModule == nil {
+			// Fall back to legacy method of building a snapshot
+			memberType.BuildSnapshot(ctx, builder, member)
+		} else {
+			s.createMemberSnapshot(ctx, builder, member, prebuiltModule)
+		}
 	}
 
 	// Create a transformer that will transform an unversioned module into a versioned module.
@@ -529,6 +558,8 @@
 	m := s.bpFile.newModule(moduleType)
 	m.AddProperty("name", name)
 
+	variant := member.Variants()[0]
+
 	if s.sdk.isInternalMember(name) {
 		// An internal member is only referenced from the sdk snapshot which is in the
 		// same package so can be marked as private.
@@ -536,7 +567,7 @@
 	} else {
 		// Extract visibility information from a member variant. All variants have the same
 		// visibility so it doesn't matter which one is used.
-		visibility := android.EffectiveVisibilityRules(s.ctx, member.Variants()[0])
+		visibility := android.EffectiveVisibilityRules(s.ctx, variant)
 		if len(visibility) != 0 {
 			m.AddProperty("visibility", visibility)
 		}
@@ -544,6 +575,14 @@
 
 	addHostDeviceSupportedProperties(&s.sdk.ModuleBase, m)
 
+	// Where available copy apex_available properties from the member.
+	if apexAware, ok := variant.(interface{ ApexAvailable() []string }); ok {
+		apexAvailable := apexAware.ApexAvailable()
+		if len(apexAvailable) > 0 {
+			m.AddProperty("apex_available", apexAvailable)
+		}
+	}
+
 	s.prebuiltModules[name] = m
 	s.prebuiltOrder = append(s.prebuiltOrder, m)
 	return m
@@ -592,6 +631,11 @@
 	return references
 }
 
+type sdkMemberRef struct {
+	memberType android.SdkMemberType
+	variant    android.SdkAware
+}
+
 var _ android.SdkMember = (*sdkMember)(nil)
 
 type sdkMember struct {
@@ -607,3 +651,285 @@
 func (m *sdkMember) Variants() []android.SdkAware {
 	return m.variants
 }
+
+type baseInfo struct {
+	Properties android.SdkMemberProperties
+}
+
+type osTypeSpecificInfo struct {
+	baseInfo
+
+	// The list of arch type specific info for this os type.
+	archTypes []*archTypeSpecificInfo
+
+	// True if the member has common arch variants for this os type.
+	commonArch bool
+}
+
+type archTypeSpecificInfo struct {
+	baseInfo
+
+	archType android.ArchType
+}
+
+func (s *sdk) createMemberSnapshot(sdkModuleContext android.ModuleContext, builder *snapshotBuilder, member *sdkMember, bpModule android.BpModule) {
+
+	memberType := member.memberType
+
+	// Group the variants by os type.
+	variantsByOsType := make(map[android.OsType][]android.SdkAware)
+	variants := member.Variants()
+	for _, variant := range variants {
+		osType := variant.Target().Os
+		variantsByOsType[osType] = append(variantsByOsType[osType], variant)
+	}
+
+	osCount := len(variantsByOsType)
+	createVariantPropertiesStruct := func(os android.OsType) android.SdkMemberProperties {
+		properties := memberType.CreateVariantPropertiesStruct()
+		base := properties.Base()
+		base.Os_count = osCount
+		base.Os = os
+		return properties
+	}
+
+	osTypeToInfo := make(map[android.OsType]*osTypeSpecificInfo)
+
+	// The set of properties that are common across all architectures and os types.
+	commonProperties := createVariantPropertiesStruct(android.CommonOS)
+
+	// The list of property structures which are os type specific but common across
+	// architectures within that os type.
+	var osSpecificPropertiesList []android.SdkMemberProperties
+
+	for osType, osTypeVariants := range variantsByOsType {
+		// Group the properties for each variant by arch type within the os.
+		osInfo := &osTypeSpecificInfo{}
+		osTypeToInfo[osType] = osInfo
+
+		// Create a structure into which properties common across the architectures in
+		// this os type will be stored. Add it to the list of os type specific yet
+		// architecture independent properties structs.
+		osInfo.Properties = createVariantPropertiesStruct(osType)
+		osSpecificPropertiesList = append(osSpecificPropertiesList, osInfo.Properties)
+
+		commonArch := false
+		for _, variant := range osTypeVariants {
+			var properties android.SdkMemberProperties
+
+			// Get the info associated with the arch type inside the os info.
+			archType := variant.Target().Arch.ArchType
+
+			if archType.Name == "common" {
+				// The arch type is common so populate the common properties directly.
+				properties = osInfo.Properties
+
+				commonArch = true
+			} else {
+				archInfo := &archTypeSpecificInfo{archType: archType}
+				properties = createVariantPropertiesStruct(osType)
+				archInfo.Properties = properties
+
+				osInfo.archTypes = append(osInfo.archTypes, archInfo)
+			}
+
+			properties.PopulateFromVariant(variant)
+		}
+
+		if commonArch {
+			if len(osTypeVariants) != 1 {
+				panic("Expected to only have 1 variant when arch type is common but found " + string(len(variants)))
+			}
+		} else {
+			var archPropertiesList []android.SdkMemberProperties
+			for _, archInfo := range osInfo.archTypes {
+				archPropertiesList = append(archPropertiesList, archInfo.Properties)
+			}
+
+			extractCommonProperties(osInfo.Properties, archPropertiesList)
+
+			// Choose setting for compile_multilib that is appropriate for the arch variants supplied.
+			var multilib string
+			archVariantCount := len(osInfo.archTypes)
+			if archVariantCount == 2 {
+				multilib = "both"
+			} else if archVariantCount == 1 {
+				if strings.HasSuffix(osInfo.archTypes[0].archType.Name, "64") {
+					multilib = "64"
+				} else {
+					multilib = "32"
+				}
+			}
+
+			osInfo.commonArch = commonArch
+			osInfo.Properties.Base().Compile_multilib = multilib
+		}
+	}
+
+	// Extract properties which are common across all architectures and os types.
+	extractCommonProperties(commonProperties, osSpecificPropertiesList)
+
+	// Add the common properties to the module.
+	commonProperties.AddToPropertySet(sdkModuleContext, builder, bpModule)
+
+	// Create a target property set into which target specific properties can be
+	// added.
+	targetPropertySet := bpModule.AddPropertySet("target")
+
+	// Iterate over the os types in a fixed order.
+	for _, osType := range s.getPossibleOsTypes() {
+		osInfo := osTypeToInfo[osType]
+		if osInfo == nil {
+			continue
+		}
+
+		var osPropertySet android.BpPropertySet
+		var archOsPrefix string
+		if len(osTypeToInfo) == 1 {
+			// There is only one os type present in the variants sp don't bother
+			// with adding target specific properties.
+
+			// Create a structure that looks like:
+			// module_type {
+			//   name: "...",
+			//   ...
+			//   <common properties>
+			//   ...
+			//   <single os type specific properties>
+			//
+			//   arch: {
+			//     <arch specific sections>
+			//   }
+			//
+			osPropertySet = bpModule
+
+			// Arch specific properties need to be added to an arch specific section
+			// within arch.
+			archOsPrefix = ""
+		} else {
+			// Create a structure that looks like:
+			// module_type {
+			//   name: "...",
+			//   ...
+			//   <common properties>
+			//   ...
+			//   target: {
+			//     <arch independent os specific sections, e.g. android>
+			//     ...
+			//     <arch and os specific sections, e.g. android_x86>
+			//   }
+			//
+			osPropertySet = targetPropertySet.AddPropertySet(osType.Name)
+
+			// Arch specific properties need to be added to an os and arch specific
+			// section prefixed with <os>_.
+			archOsPrefix = osType.Name + "_"
+		}
+
+		osInfo.Properties.AddToPropertySet(sdkModuleContext, builder, osPropertySet)
+		if !osInfo.commonArch {
+			// Either add the arch specific sections into the target or arch sections
+			// depending on whether they will also be os specific.
+			var archPropertySet android.BpPropertySet
+			if archOsPrefix == "" {
+				archPropertySet = osPropertySet.AddPropertySet("arch")
+			} else {
+				archPropertySet = targetPropertySet
+			}
+
+			// Add arch (and possibly os) specific sections for each set of
+			// arch (and possibly os) specific properties.
+			for _, av := range osInfo.archTypes {
+				archTypePropertySet := archPropertySet.AddPropertySet(archOsPrefix + av.archType.Name)
+
+				av.Properties.AddToPropertySet(sdkModuleContext, builder, archTypePropertySet)
+			}
+		}
+	}
+
+	memberType.FinalizeModule(sdkModuleContext, builder, member, bpModule)
+}
+
+// Compute the list of possible os types that this sdk could support.
+func (s *sdk) getPossibleOsTypes() []android.OsType {
+	var osTypes []android.OsType
+	for _, osType := range android.OsTypeList {
+		if s.DeviceSupported() {
+			if osType.Class == android.Device && osType != android.Fuchsia {
+				osTypes = append(osTypes, osType)
+			}
+		}
+		if s.HostSupported() {
+			if osType.Class == android.Host || osType.Class == android.HostCross {
+				osTypes = append(osTypes, osType)
+			}
+		}
+	}
+	sort.SliceStable(osTypes, func(i, j int) bool { return osTypes[i].Name < osTypes[j].Name })
+	return osTypes
+}
+
+// Extract common properties from a slice of property structures of the same type.
+//
+// All the property structures must be of the same type.
+// commonProperties - must be a pointer to the structure into which common properties will be added.
+// inputPropertiesSlice - must be a slice of input properties structures.
+//
+// Iterates over each exported field (capitalized name) and checks to see whether they
+// have the same value (using DeepEquals) across all the input properties. If it does not then no
+// change is made. Otherwise, the common value is stored in the field in the commonProperties
+// and the field in each of the input properties structure is set to its default value.
+func extractCommonProperties(commonProperties interface{}, inputPropertiesSlice interface{}) {
+	commonPropertiesValue := reflect.ValueOf(commonProperties)
+	commonStructValue := commonPropertiesValue.Elem()
+	propertiesStructType := commonStructValue.Type()
+
+	// Create an empty structure from which default values for the field can be copied.
+	emptyStructValue := reflect.New(propertiesStructType).Elem()
+
+	for f := 0; f < propertiesStructType.NumField(); f++ {
+		// Check to see if all the structures have the same value for the field. The commonValue
+		// is nil on entry to the loop and if it is nil on exit then there is no common value,
+		// otherwise it points to the common value.
+		var commonValue *reflect.Value
+		sliceValue := reflect.ValueOf(inputPropertiesSlice)
+
+		field := propertiesStructType.Field(f)
+		if field.Name == "SdkMemberPropertiesBase" {
+			continue
+		}
+
+		for i := 0; i < sliceValue.Len(); i++ {
+			structValue := sliceValue.Index(i).Elem().Elem()
+			fieldValue := structValue.Field(f)
+			if !fieldValue.CanInterface() {
+				// The field is not exported so ignore it.
+				continue
+			}
+
+			if commonValue == nil {
+				// Use the first value as the commonProperties value.
+				commonValue = &fieldValue
+			} else {
+				// If the value does not match the current common value then there is
+				// no value in common so break out.
+				if !reflect.DeepEqual(fieldValue.Interface(), commonValue.Interface()) {
+					commonValue = nil
+					break
+				}
+			}
+		}
+
+		// If the fields all have a common value then store it in the common struct field
+		// and set the input struct's field to the empty value.
+		if commonValue != nil {
+			emptyValue := emptyStructValue.Field(f)
+			commonStructValue.Field(f).Set(*commonValue)
+			for i := 0; i < sliceValue.Len(); i++ {
+				structValue := sliceValue.Index(i).Elem().Elem()
+				fieldValue := structValue.Field(f)
+				fieldValue.Set(emptyValue)
+			}
+		}
+	}
+}
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index bfe662d..5717401 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -74,28 +74,27 @@
 }
 
 var Configuration = map[string]PathConfig{
-	"bash":     Allowed,
-	"dd":       Allowed,
-	"diff":     Allowed,
-	"dlv":      Allowed,
-	"expr":     Allowed,
-	"fuser":    Allowed,
-	"getopt":   Allowed,
-	"git":      Allowed,
-	"hexdump":  Allowed,
-	"jar":      Allowed,
-	"java":     Allowed,
-	"javap":    Allowed,
-	"lsof":     Allowed,
-	"openssl":  Allowed,
-	"patch":    Allowed,
-	"pstree":   Allowed,
-	"python3":  Allowed,
-	"rsync":    Allowed,
-	"sh":       Allowed,
-	"tr":       Allowed,
-	"unzip":    Allowed,
-	"zip":      Allowed,
+	"bash":    Allowed,
+	"dd":      Allowed,
+	"diff":    Allowed,
+	"dlv":     Allowed,
+	"expr":    Allowed,
+	"fuser":   Allowed,
+	"getopt":  Allowed,
+	"git":     Allowed,
+	"hexdump": Allowed,
+	"jar":     Allowed,
+	"java":    Allowed,
+	"javap":   Allowed,
+	"lsof":    Allowed,
+	"openssl": Allowed,
+	"patch":   Allowed,
+	"pstree":  Allowed,
+	"rsync":   Allowed,
+	"sh":      Allowed,
+	"tr":      Allowed,
+	"unzip":   Allowed,
+	"zip":     Allowed,
 
 	// Host toolchain is removed. In-tree toolchain should be used instead.
 	// GCC also can't find cc1 with this implementation.
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index 11ff667..2de772b 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -90,10 +90,7 @@
 			return
 		}
 
-		c.ctx.Println("Build sandboxing disabled due to nsjail error. This may become fatal in the future.")
-		c.ctx.Println("Please let us know why nsjail doesn't work in your environment at:")
-		c.ctx.Println("  https://groups.google.com/forum/#!forum/android-building")
-		c.ctx.Println("  https://issuetracker.google.com/issues/new?component=381517")
+		c.ctx.Println("Build sandboxing disabled due to nsjail error.")
 
 		for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
 			c.ctx.Verboseln(line)