Merge "Add bugprone to global clang-tidy default checks"
diff --git a/android/arch.go b/android/arch.go
index 73a490d..922548e 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -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},
@@ -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:
@@ -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..c4b1073 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 {
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/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_test.go b/apex/apex_test.go
index 6d9ad26..674446a 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -1389,13 +1389,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 +1409,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__")
@@ -3301,7 +3325,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 +3353,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/cc.go b/cc/cc.go
index 76f1f96..cc54f7a 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -2740,19 +2740,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 +2824,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..8acc817 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) {
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/java/java.go b/java/java.go
index 8d58a90..135ae51 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]
 		},
 	})
 
@@ -1883,6 +1889,10 @@
 
 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,11 +1904,7 @@
 	return ok
 }
 
-func (mt *librarySdkMemberType) buildSnapshot(
-	sdkModuleContext android.ModuleContext,
-	builder android.SnapshotBuilder,
-	member android.SdkMember,
-	jarToExportGetter func(j *Library) android.Path) {
+func (mt *librarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
 
 	variants := member.Variants()
 	if len(variants) != 1 {
@@ -1910,7 +1916,7 @@
 	variant := variants[0]
 	j := variant.(*Library)
 
-	exportedJar := jarToExportGetter(j)
+	exportedJar := mt.jarToExportGetter(j)
 	snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(member)
 	builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
 
@@ -1926,43 +1932,19 @@
 	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.
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..45b548c 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"
 )
 
@@ -54,7 +55,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 +251,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 +288,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -356,7 +357,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mymodule_exports", "android_common", "",
+	result.CheckSnapshot("mymodule_exports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -422,7 +423,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -510,7 +511,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -597,7 +598,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "android_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -685,7 +686,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -784,7 +785,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -856,7 +857,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -909,7 +910,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
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..9046eec 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.
 
@@ -250,7 +250,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "android_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -305,7 +305,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -356,7 +356,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "android_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -409,7 +409,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -503,7 +503,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "android_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -526,7 +526,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 +552,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -580,7 +580,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 +612,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -702,7 +702,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..14e44bf 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,29 @@
 }
 
 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.
+		s.snapshotFile = android.OptionalPathForPath(s.buildSnapshot(ctx, sdkVariants))
 	}
 }
 
@@ -320,7 +346,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..d06401a 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.
 
diff --git a/sdk/testing.go b/sdk/testing.go
index 7352c74..3ae8b5d 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -188,15 +188,20 @@
 
 	buildParams := sdk.BuildParamsForTests()
 	copyRules := &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)
-			// 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)
+			output := bp.Output
+			// Only check copies into the snapshot directory.
+			if strings.HasPrefix(output.String(), snapshotDirPrefix) {
+				// Get source relative to build directory.
+				src := android.NormalizePathForTesting(bp.Input)
+				// Get destination relative to the snapshot root
+				dest := output.Rel()
+				_, _ = fmt.Fprintf(copyRules, "%s -> %s\n", src, dest)
+				info.snapshotContents = append(info.snapshotContents, dest)
+			}
 
 		case repackageZip.String():
 			// Add the destdir to the snapshot contents as that is effectively where
@@ -246,9 +251,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)
diff --git a/sdk/update.go b/sdk/update.go
index d211e80..087b8bc 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -104,18 +104,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 +117,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 +127,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 +222,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,7 +249,7 @@
 	}
 	s.builderForTests = builder
 
-	members, multilib := s.collectMembers(ctx)
+	members, multilib := s.organizeMembers(ctx, memberRefs)
 	for _, member := range members {
 		member.memberType.BuildSnapshot(ctx, builder, member)
 	}
@@ -592,6 +613,11 @@
 	return references
 }
 
+type sdkMemberRef struct {
+	memberType android.SdkMemberType
+	variant    android.SdkAware
+}
+
 var _ android.SdkMember = (*sdkMember)(nil)
 
 type sdkMember struct {
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)