Merge "Annotate FIXME for b/200678898"
diff --git a/android/Android.bp b/android/Android.bp
index f3a3850..6450a06 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -110,6 +110,7 @@
         "paths_test.go",
         "prebuilt_test.go",
         "rule_builder_test.go",
+        "sdk_test.go",
         "singleton_module_test.go",
         "soong_config_modules_test.go",
         "util_test.go",
diff --git a/android/bazel.go b/android/bazel.go
index 8341e57..bfd0f90 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -150,17 +150,23 @@
 		"build/bazel/platforms":/* recursive = */ true,
 		"build/bazel/product_variables":/* recursive = */ true,
 		"build/bazel_common_rules":/* recursive = */ true,
+		"build/make/tools":/* recursive = */ true,
 		"build/pesto":/* recursive = */ true,
 
 		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
 		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
 		"external/bazelbuild-rules_android":/* recursive = */ true,
 		"external/bazel-skylib":/* recursive = */ true,
+		"external/guava":/* recursive = */ true,
+		"external/error_prone":/* recursive = */ true,
+		"external/jsr305":/* recursive = */ true,
+		"frameworks/ex/common":/* recursive = */ true,
 
 		"prebuilts/sdk":/* recursive = */ false,
 		"prebuilts/sdk/tools":/* recursive = */ false,
 		"prebuilts/r8":/* recursive = */ false,
 		"packages/apps/Music":/* recursive = */ true,
+		"packages/apps/QuickSearchBox":/* recursive = */ true,
 	}
 
 	// Configure modules in these directories to enable bp2build_available: true or false by default.
@@ -259,7 +265,6 @@
 	// Per-module denylist of cc_library modules to only generate the static
 	// variant if their shared variant isn't ready or buildable by Bazel.
 	bp2buildCcLibraryStaticOnlyList = []string{
-		"libstdc++",    // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno
 		"libjemalloc5", // http://b/188503688, cc_library, `target: { android: { enabled: false } }` for android targets.
 	}
 
@@ -294,8 +299,8 @@
 	}
 }
 
-func GenerateCcLibraryStaticOnly(ctx BazelConversionPathContext) bool {
-	return bp2buildCcLibraryStaticOnly[ctx.Module().Name()]
+func GenerateCcLibraryStaticOnly(moduleName string) bool {
+	return bp2buildCcLibraryStaticOnly[moduleName]
 }
 
 func ShouldKeepExistingBuildFileForDir(dir string) bool {
@@ -325,7 +330,7 @@
 		return false
 	}
 
-	if GenerateCcLibraryStaticOnly(ctx) {
+	if GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
 		// Don't use partially-converted cc_library targets in mixed builds,
 		// since mixed builds would generally rely on both static and shared
 		// variants of a cc_library.
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index ccbc156..b5746f7 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -15,11 +15,12 @@
 package android
 
 import (
-	"android/soong/bazel"
 	"fmt"
 	"path/filepath"
 	"strings"
 
+	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
 )
@@ -84,24 +85,8 @@
 // BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
 // or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
 // module within the given ctx.
-func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
-	return bazelLabelForModuleDeps(ctx, modules, false)
-}
-
-// BazelLabelForModuleWholeDeps expects a list of references to other modules, ("<module>"
-// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
-// module within the given ctx, where prebuilt dependencies will be appended with _alwayslink so
-// they can be handled as whole static libraries.
-func BazelLabelForModuleWholeDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
-	return bazelLabelForModuleDeps(ctx, modules, true)
-}
-
-// BazelLabelForModuleDepsExcludes expects two lists: modules (containing modules to include in the
-// list), and excludes (modules to exclude from the list). Both of these should contain references
-// to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label list which
-// corresponds to dependencies on the module within the given ctx, and the excluded dependencies.
-func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
-	return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, false)
+func BazelLabelForModuleDeps(ctx TopDownMutatorContext, modules []string) bazel.LabelList {
+	return BazelLabelForModuleDepsWithFn(ctx, modules, BazelModuleLabel)
 }
 
 // BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in
@@ -110,11 +95,15 @@
 // list which corresponds to dependencies on the module within the given ctx, and the excluded
 // dependencies.  Prebuilt dependencies will be appended with _alwayslink so they can be handled as
 // whole static libraries.
-func BazelLabelForModuleWholeDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
-	return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, true)
+func BazelLabelForModuleDepsExcludes(ctx TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+	return BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, BazelModuleLabel)
 }
 
-func bazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string, isWholeLibs bool) bazel.LabelList {
+// BazelLabelForModuleDepsWithFn expects a list of reference to other modules, ("<module>"
+// or ":<module>") and applies moduleToLabelFn to determine and return a Bazel-compatible label
+// which corresponds to dependencies on the module within the given ctx.
+func BazelLabelForModuleDepsWithFn(ctx TopDownMutatorContext, modules []string,
+	moduleToLabelFn func(TopDownMutatorContext, blueprint.Module) string) bazel.LabelList {
 	var labels bazel.LabelList
 	// In some cases, a nil string list is different than an explicitly empty list.
 	if len(modules) == 0 && modules != nil {
@@ -127,7 +116,7 @@
 			module = ":" + module
 		}
 		if m, t := SrcIsModuleWithTag(module); m != "" {
-			l := getOtherModuleLabel(ctx, m, t, isWholeLibs)
+			l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn)
 			l.OriginalModuleName = bpText
 			labels.Includes = append(labels.Includes, l)
 		} else {
@@ -137,23 +126,29 @@
 	return labels
 }
 
-func bazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string, isWholeLibs bool) bazel.LabelList {
-	moduleLabels := bazelLabelForModuleDeps(ctx, RemoveListFromList(modules, excludes), isWholeLibs)
+// BazelLabelForModuleDepsExcludesWithFn expects two lists: modules (containing modules to include in the
+// list), and excludes (modules to exclude from the list). Both of these should contain references
+// to other modules, ("<module>" or ":<module>"). It applies moduleToLabelFn to determine and return a
+// Bazel-compatible label list which corresponds to dependencies on the module within the given ctx, and
+// the excluded dependencies.
+func BazelLabelForModuleDepsExcludesWithFn(ctx TopDownMutatorContext, modules, excludes []string,
+	moduleToLabelFn func(TopDownMutatorContext, blueprint.Module) string) bazel.LabelList {
+	moduleLabels := BazelLabelForModuleDepsWithFn(ctx, RemoveListFromList(modules, excludes), moduleToLabelFn)
 	if len(excludes) == 0 {
 		return moduleLabels
 	}
-	excludeLabels := bazelLabelForModuleDeps(ctx, excludes, isWholeLibs)
+	excludeLabels := BazelLabelForModuleDepsWithFn(ctx, excludes, moduleToLabelFn)
 	return bazel.LabelList{
 		Includes: moduleLabels.Includes,
 		Excludes: excludeLabels.Includes,
 	}
 }
 
-func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
+func BazelLabelForModuleSrcSingle(ctx TopDownMutatorContext, path string) bazel.Label {
 	return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0]
 }
 
-func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label {
+func BazelLabelForModuleDepSingle(ctx TopDownMutatorContext, path string) bazel.Label {
 	return BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes[0]
 }
 
@@ -163,7 +158,7 @@
 // relative if within the same package).
 // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
 // will have already been handled by the path_deps mutator.
-func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
+func BazelLabelForModuleSrc(ctx TopDownMutatorContext, paths []string) bazel.LabelList {
 	return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
 }
 
@@ -173,7 +168,7 @@
 // (absolute if in a different package or relative if within the same package).
 // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
 // will have already been handled by the path_deps mutator.
-func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
+func BazelLabelForModuleSrcExcludes(ctx TopDownMutatorContext, paths, excludes []string) bazel.LabelList {
 	excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
 	excluded := make([]string, 0, len(excludeLabels.Includes))
 	for _, e := range excludeLabels.Includes {
@@ -293,7 +288,7 @@
 // Properties passed as the paths or excludes argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on other modules will have already been handled by the
 // path_deps mutator.
-func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
+func expandSrcsForBazel(ctx TopDownMutatorContext, paths, expandedExcludes []string) bazel.LabelList {
 	if paths == nil {
 		return bazel.LabelList{}
 	}
@@ -310,7 +305,7 @@
 
 	for _, p := range paths {
 		if m, tag := SrcIsModuleWithTag(p); m != "" {
-			l := getOtherModuleLabel(ctx, m, tag, false)
+			l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel)
 			if !InList(l.Label, expandedExcludes) {
 				l.OriginalModuleName = fmt.Sprintf(":%s", m)
 				labels.Includes = append(labels.Includes, l)
@@ -341,7 +336,8 @@
 // getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
 // module. The label will be relative to the current directory if appropriate. The dependency must
 // already be resolved by either deps mutator or path deps mutator.
-func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string, isWholeLibs bool) bazel.Label {
+func getOtherModuleLabel(ctx TopDownMutatorContext, dep, tag string,
+	labelFromModule func(TopDownMutatorContext, blueprint.Module) string) bazel.Label {
 	m, _ := ctx.ModuleFromName(dep)
 	if m == nil {
 		panic(fmt.Errorf("No module named %q found, but was a direct dep of %q", dep, ctx.Module().Name()))
@@ -349,13 +345,11 @@
 	if !convertedToBazel(ctx, m) {
 		ctx.AddUnconvertedBp2buildDep(dep)
 	}
-	otherLabel := bazelModuleLabel(ctx, m, tag)
-	label := bazelModuleLabel(ctx, ctx.Module(), "")
-	if isWholeLibs {
-		if m, ok := m.(Module); ok && IsModulePrebuilt(m) {
-			otherLabel += "_alwayslink"
-		}
-	}
+	label := BazelModuleLabel(ctx, ctx.Module())
+	otherLabel := labelFromModule(ctx, m)
+
+	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
+
 	if samePackage(label, otherLabel) {
 		otherLabel = bazelShortLabel(otherLabel)
 	}
@@ -365,7 +359,7 @@
 	}
 }
 
-func bazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module, tag string) string {
+func BazelModuleLabel(ctx TopDownMutatorContext, module blueprint.Module) string {
 	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
 	if !convertedToBazel(ctx, module) {
 		return bp2buildModuleLabel(ctx, module)
diff --git a/android/neverallow.go b/android/neverallow.go
index 19b58a7..a91d523 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -152,7 +152,7 @@
 	javaDeviceForHostProjectsAllowedList := []string{
 		"external/guava",
 		"external/robolectric-shadows",
-		"framework/layoutlib",
+		"frameworks/layoutlib",
 	}
 
 	return []Rule{
diff --git a/android/sdk.go b/android/sdk.go
index 100f63b..1d63d7a 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"fmt"
 	"sort"
 	"strings"
 
@@ -376,6 +377,224 @@
 
 var _ BpPrintable = BpPrintableBase{}
 
+// sdkRegisterable defines the interface that must be implemented by objects that can be registered
+// in an sdkRegistry.
+type sdkRegisterable interface {
+	// SdkPropertyName returns the name of the corresponding property on an sdk module.
+	SdkPropertyName() string
+}
+
+// sdkRegistry provides support for registering and retrieving objects that define properties for
+// use by sdk and module_exports module types.
+type sdkRegistry struct {
+	// The list of registered objects sorted by property name.
+	list []sdkRegisterable
+}
+
+// copyAndAppend creates a new sdkRegistry that includes all the traits registered in
+// this registry plus the supplied trait.
+func (r *sdkRegistry) copyAndAppend(registerable sdkRegisterable) *sdkRegistry {
+	oldList := r.list
+
+	// Make sure that list does not already contain the property. Uses a simple linear search instead
+	// of a binary search even though the list is sorted. That is because the number of items in the
+	// list is small and so not worth the overhead of a binary search.
+	found := false
+	newPropertyName := registerable.SdkPropertyName()
+	for _, r := range oldList {
+		if r.SdkPropertyName() == newPropertyName {
+			found = true
+			break
+		}
+	}
+	if found {
+		names := []string{}
+		for _, r := range oldList {
+			names = append(names, r.SdkPropertyName())
+		}
+		panic(fmt.Errorf("duplicate properties found, %q already exists in %q", newPropertyName, names))
+	}
+
+	// Copy the slice just in case this is being read while being modified, e.g. when testing.
+	list := make([]sdkRegisterable, 0, len(oldList)+1)
+	list = append(list, oldList...)
+	list = append(list, registerable)
+
+	// Sort the registered objects by their property name to ensure that registry order has no effect
+	// on behavior.
+	sort.Slice(list, func(i1, i2 int) bool {
+		t1 := list[i1]
+		t2 := list[i2]
+
+		return t1.SdkPropertyName() < t2.SdkPropertyName()
+	})
+
+	// Create a new registry so the pointer uniquely identifies the set of registered types.
+	return &sdkRegistry{
+		list: list,
+	}
+}
+
+// registeredObjects returns the list of registered instances.
+func (r *sdkRegistry) registeredObjects() []sdkRegisterable {
+	return r.list
+}
+
+// uniqueOnceKey returns a key that uniquely identifies this instance and can be used with
+// OncePer.Once
+func (r *sdkRegistry) uniqueOnceKey() OnceKey {
+	// Use the pointer to the registry as the unique key. The pointer is used because it is guaranteed
+	// to uniquely identify the contained list. The list itself cannot be used as slices are not
+	// comparable. Using the pointer does mean that two separate registries with identical lists would
+	// have different keys and so cause whatever information is cached to be created multiple times.
+	// However, that is not an issue in practice as it should not occur outside tests. Constructing a
+	// string representation of the list to use instead would avoid that but is an unnecessary
+	// complication that provides no significant benefit.
+	return NewCustomOnceKey(r)
+}
+
+// SdkMemberTrait represents a trait that members of an sdk module can contribute to the sdk
+// snapshot.
+//
+// A trait is simply a characteristic of sdk member that is not required by default which may be
+// required for some members but not others. Traits can cause additional information to be output
+// to the sdk snapshot or replace the default information exported for a member with something else.
+// e.g.
+// * By default cc libraries only export the default image variants to the SDK. However, for some
+//   members it may be necessary to export specific image variants, e.g. vendor, or recovery.
+// * By default cc libraries export all the configured architecture variants except for the native
+//   bridge architecture variants. However, for some members it may be necessary to export the
+//   native bridge architecture variants as well.
+// * By default cc libraries export the platform variant (i.e. sdk:). However, for some members it
+//   may be necessary to export the sdk variant (i.e. sdk:sdk).
+//
+// A sdk can request a module to provide no traits, one trait or a collection of traits. The exact
+// behavior of a trait is determined by how SdkMemberType implementations handle the traits. A trait
+// could be specific to one SdkMemberType or many. Some trait combinations could be incompatible.
+//
+// The sdk module type will create a special traits structure that contains a property for each
+// trait registered with RegisterSdkMemberTrait(). The property names are those returned from
+// SdkPropertyName(). Each property contains a list of modules that are required to have that trait.
+// e.g. something like this:
+//
+//   sdk {
+//     name: "sdk",
+//     ...
+//     traits: {
+//       recovery_image: ["module1", "module4", "module5"],
+//       native_bridge: ["module1", "module2"],
+//       native_sdk: ["module1", "module3"],
+//       ...
+//     },
+//     ...
+//   }
+type SdkMemberTrait interface {
+	// SdkPropertyName returns the name of the traits property on an sdk module.
+	SdkPropertyName() string
+}
+
+var _ sdkRegisterable = (SdkMemberTrait)(nil)
+
+// SdkMemberTraitBase is the base struct that must be embedded within any type that implements
+// SdkMemberTrait.
+type SdkMemberTraitBase struct {
+	// PropertyName is the name of the property
+	PropertyName string
+}
+
+func (b *SdkMemberTraitBase) SdkPropertyName() string {
+	return b.PropertyName
+}
+
+// SdkMemberTraitSet is a set of SdkMemberTrait instances.
+type SdkMemberTraitSet interface {
+	// Empty returns true if this set is empty.
+	Empty() bool
+
+	// Contains returns true if this set contains the specified trait.
+	Contains(trait SdkMemberTrait) bool
+
+	// Subtract returns a new set containing all elements of this set except for those in the
+	// other set.
+	Subtract(other SdkMemberTraitSet) SdkMemberTraitSet
+
+	// String returns a string representation of the set and its contents.
+	String() string
+}
+
+func NewSdkMemberTraitSet(traits []SdkMemberTrait) SdkMemberTraitSet {
+	if len(traits) == 0 {
+		return EmptySdkMemberTraitSet()
+	}
+
+	m := sdkMemberTraitSet{}
+	for _, trait := range traits {
+		m[trait] = true
+	}
+	return m
+}
+
+func EmptySdkMemberTraitSet() SdkMemberTraitSet {
+	return (sdkMemberTraitSet)(nil)
+}
+
+type sdkMemberTraitSet map[SdkMemberTrait]bool
+
+var _ SdkMemberTraitSet = (sdkMemberTraitSet{})
+
+func (s sdkMemberTraitSet) Empty() bool {
+	return len(s) == 0
+}
+
+func (s sdkMemberTraitSet) Contains(trait SdkMemberTrait) bool {
+	return s[trait]
+}
+
+func (s sdkMemberTraitSet) Subtract(other SdkMemberTraitSet) SdkMemberTraitSet {
+	if other.Empty() {
+		return s
+	}
+
+	var remainder []SdkMemberTrait
+	for trait, _ := range s {
+		if !other.Contains(trait) {
+			remainder = append(remainder, trait)
+		}
+	}
+
+	return NewSdkMemberTraitSet(remainder)
+}
+
+func (s sdkMemberTraitSet) String() string {
+	list := []string{}
+	for trait, _ := range s {
+		list = append(list, trait.SdkPropertyName())
+	}
+	sort.Strings(list)
+	return fmt.Sprintf("[%s]", strings.Join(list, ","))
+}
+
+var registeredSdkMemberTraits = &sdkRegistry{}
+
+// RegisteredSdkMemberTraits returns a OnceKey and a sorted list of registered traits.
+//
+// The key uniquely identifies the array of traits and can be used with OncePer.Once() to cache
+// information derived from the array of traits.
+func RegisteredSdkMemberTraits() (OnceKey, []SdkMemberTrait) {
+	registerables := registeredSdkMemberTraits.registeredObjects()
+	traits := make([]SdkMemberTrait, len(registerables))
+	for i, registerable := range registerables {
+		traits[i] = registerable.(SdkMemberTrait)
+	}
+	return registeredSdkMemberTraits.uniqueOnceKey(), traits
+}
+
+// RegisterSdkMemberTrait registers an SdkMemberTrait object to allow them to be used in the
+// module_exports, module_exports_snapshot, sdk and sdk_snapshot module types.
+func RegisterSdkMemberTrait(trait SdkMemberTrait) {
+	registeredSdkMemberTraits = registeredSdkMemberTraits.copyAndAppend(trait)
+}
+
 // SdkMember is an individual member of the SDK.
 //
 // It includes all of the variants that the SDK depends upon.
@@ -541,12 +760,25 @@
 	// CreateVariantPropertiesStruct creates a structure into which variant specific properties can be
 	// added.
 	CreateVariantPropertiesStruct() SdkMemberProperties
+
+	// SupportedTraits returns the set of traits supported by this member type.
+	SupportedTraits() SdkMemberTraitSet
 }
 
+var _ sdkRegisterable = (SdkMemberType)(nil)
+
 // SdkDependencyContext provides access to information needed by the SdkMemberType.AddDependencies()
 // implementations.
 type SdkDependencyContext interface {
 	BottomUpMutatorContext
+
+	// RequiredTraits returns the set of SdkMemberTrait instances that the sdk requires the named
+	// member to provide.
+	RequiredTraits(name string) SdkMemberTraitSet
+
+	// RequiresTrait returns true if the sdk requires the member with the supplied name to provide the
+	// supplied trait.
+	RequiresTrait(name string, trait SdkMemberTrait) bool
 }
 
 // SdkMemberTypeBase is the base type for SdkMemberType implementations and must be embedded in any
@@ -565,6 +797,9 @@
 	// module type in its SdkMemberType.AddPrebuiltModule() method. That prevents the sdk snapshot
 	// code from automatically adding a prefer: true flag.
 	UseSourceModuleTypeInSnapshot bool
+
+	// The list of supported traits.
+	Traits []SdkMemberTrait
 }
 
 func (b *SdkMemberTypeBase) SdkPropertyName() string {
@@ -587,60 +822,52 @@
 	return b.UseSourceModuleTypeInSnapshot
 }
 
-// SdkMemberTypesRegistry encapsulates the information about registered SdkMemberTypes.
-type SdkMemberTypesRegistry struct {
-	// The list of types sorted by property name.
-	list []SdkMemberType
+func (b *SdkMemberTypeBase) SupportedTraits() SdkMemberTraitSet {
+	return NewSdkMemberTraitSet(b.Traits)
 }
 
-func (r *SdkMemberTypesRegistry) copyAndAppend(memberType SdkMemberType) *SdkMemberTypesRegistry {
-	oldList := r.list
+// registeredModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports
+// modules.
+var registeredModuleExportsMemberTypes = &sdkRegistry{}
 
-	// Copy the slice just in case this is being read while being modified, e.g. when testing.
-	list := make([]SdkMemberType, 0, len(oldList)+1)
-	list = append(list, oldList...)
-	list = append(list, memberType)
+// registeredSdkMemberTypes is the set of registered registeredSdkMemberTypes for sdk modules.
+var registeredSdkMemberTypes = &sdkRegistry{}
 
-	// Sort the member types by their property name to ensure that registry order has no effect
-	// on behavior.
-	sort.Slice(list, func(i1, i2 int) bool {
-		t1 := list[i1]
-		t2 := list[i2]
-
-		return t1.SdkPropertyName() < t2.SdkPropertyName()
-	})
-
-	// Create a new registry so the pointer uniquely identifies the set of registered types.
-	return &SdkMemberTypesRegistry{
-		list: list,
+// RegisteredSdkMemberTypes returns a OnceKey and a sorted list of registered types.
+//
+// If moduleExports is true then the slice of types includes all registered types that can be used
+// with the module_exports and module_exports_snapshot module types. Otherwise, the slice of types
+// only includes those registered types that can be used with the sdk and sdk_snapshot module
+// types.
+//
+// The key uniquely identifies the array of types and can be used with OncePer.Once() to cache
+// information derived from the array of types.
+func RegisteredSdkMemberTypes(moduleExports bool) (OnceKey, []SdkMemberType) {
+	var registry *sdkRegistry
+	if moduleExports {
+		registry = registeredModuleExportsMemberTypes
+	} else {
+		registry = registeredSdkMemberTypes
 	}
+
+	registerables := registry.registeredObjects()
+	types := make([]SdkMemberType, len(registerables))
+	for i, registerable := range registerables {
+		types[i] = registerable.(SdkMemberType)
+	}
+	return registry.uniqueOnceKey(), types
 }
 
-func (r *SdkMemberTypesRegistry) RegisteredTypes() []SdkMemberType {
-	return r.list
-}
-
-func (r *SdkMemberTypesRegistry) UniqueOnceKey() OnceKey {
-	// Use the pointer to the registry as the unique key.
-	return NewCustomOnceKey(r)
-}
-
-// ModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports modules.
-var ModuleExportsMemberTypes = &SdkMemberTypesRegistry{}
-
-// SdkMemberTypes is the set of registered SdkMemberTypes for sdk modules.
-var SdkMemberTypes = &SdkMemberTypesRegistry{}
-
 // RegisterSdkMemberType registers an SdkMemberType object to allow them to be used in the
 // module_exports, module_exports_snapshot and (depending on the value returned from
 // SdkMemberType.UsableWithSdkAndSdkSnapshot) the sdk and sdk_snapshot module types.
 func RegisterSdkMemberType(memberType SdkMemberType) {
 	// All member types are usable with module_exports.
-	ModuleExportsMemberTypes = ModuleExportsMemberTypes.copyAndAppend(memberType)
+	registeredModuleExportsMemberTypes = registeredModuleExportsMemberTypes.copyAndAppend(memberType)
 
 	// Only those that explicitly indicate it are usable with sdk.
 	if memberType.UsableWithSdkAndSdkSnapshot() {
-		SdkMemberTypes = SdkMemberTypes.copyAndAppend(memberType)
+		registeredSdkMemberTypes = registeredSdkMemberTypes.copyAndAppend(memberType)
 	}
 }
 
@@ -733,6 +960,9 @@
 	// Provided for use by sdk members to create a member specific location within the snapshot
 	// into which to copy the prebuilt files.
 	Name() string
+
+	// RequiresTrait returns true if this member is expected to provide the specified trait.
+	RequiresTrait(trait SdkMemberTrait) bool
 }
 
 // ExportedComponentsInfo contains information about the components that this module exports to an
diff --git a/android/sdk_test.go b/android/sdk_test.go
new file mode 100644
index 0000000..51aeb31
--- /dev/null
+++ b/android/sdk_test.go
@@ -0,0 +1,53 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import "testing"
+
+type testSdkRegisterable struct {
+	name string
+}
+
+func (t *testSdkRegisterable) SdkPropertyName() string {
+	return t.name
+}
+
+var _ sdkRegisterable = &testSdkRegisterable{}
+
+func TestSdkRegistry(t *testing.T) {
+	alpha := &testSdkRegisterable{"alpha"}
+	beta := &testSdkRegisterable{"beta"}
+	betaDup := &testSdkRegisterable{"beta"}
+
+	// Make sure that an empty registry is empty.
+	emptyRegistry := &sdkRegistry{}
+	AssertDeepEquals(t, "emptyRegistry should be empty", ([]sdkRegisterable)(nil), emptyRegistry.registeredObjects())
+
+	// Add beta to the empty registry to create another registry, check that it contains beta and make
+	// sure that it does not affect the creating registry.
+	registry1 := emptyRegistry.copyAndAppend(beta)
+	AssertDeepEquals(t, "emptyRegistry should still be empty", ([]sdkRegisterable)(nil), emptyRegistry.registeredObjects())
+	AssertDeepEquals(t, "registry1 should contain beta", []sdkRegisterable{beta}, registry1.registeredObjects())
+
+	// Add alpha to the registry containing beta to create another registry, check that it contains
+	// alpha,beta (in order) and make sure that it does not affect the creating registry.
+	registry2 := registry1.copyAndAppend(alpha)
+	AssertDeepEquals(t, "registry1 should still contain beta", []sdkRegisterable{beta}, registry1.registeredObjects())
+	AssertDeepEquals(t, "registry2 should contain alpha,beta", []sdkRegisterable{alpha, beta}, registry2.registeredObjects())
+
+	AssertPanicMessageContains(t, "duplicate beta should be detected", `"beta" already exists in ["alpha" "beta"]`, func() {
+		registry2.copyAndAppend(betaDup)
+	})
+}
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index d731f3e..0bd71c6 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -111,14 +111,20 @@
 rootStaticArchives = []
 linker_inputs = cc_info.linking_context.linker_inputs.to_list()
 
-for linker_input in linker_inputs:
-  for library in linker_input.libraries:
-    for object in library.objects:
-      ccObjectFiles += [object.path]
-    if library.static_library:
-      staticLibraries.append(library.static_library.path)
-      if linker_input.owner == target.label:
-        rootStaticArchives.append(library.static_library.path)
+static_info_tag = "//build/bazel/rules:cc_library_static.bzl%CcStaticLibraryInfo"
+if static_info_tag in providers(target):
+  static_info = providers(target)[static_info_tag]
+  ccObjectFiles = [f.path for f in static_info.objects]
+  rootStaticArchives = [static_info.root_static_archive.path]
+else:
+  for linker_input in linker_inputs:
+    for library in linker_input.libraries:
+      for object in library.objects:
+        ccObjectFiles += [object.path]
+      if library.static_library:
+        staticLibraries.append(library.static_library.path)
+        if linker_input.owner == target.label:
+          rootStaticArchives.append(library.static_library.path)
 
 rootDynamicLibraries = []
 
@@ -141,9 +147,10 @@
   system_includes,
   rootStaticArchives,
   rootDynamicLibraries,
+  [toc_file]
 ]
 
-return "|".join([", ".join(r) for r in returns] + [toc_file])`
+return "|".join([", ".join(r) for r in returns])`
 }
 
 // ParseResult returns a value obtained by parsing the result of the request's Starlark function.
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 07f492e..b1a6e2c 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -261,7 +261,7 @@
 
 	// Simple metrics tracking for bp2build
 	metrics := CodegenMetrics{
-		RuleClassCount: make(map[string]int),
+		ruleClassCount: make(map[string]int),
 	}
 
 	dirs := make(map[string]bool)
@@ -277,14 +277,28 @@
 
 		switch ctx.Mode() {
 		case Bp2Build:
+			// There are two main ways of converting a Soong module to Bazel:
+			// 1) Manually handcrafting a Bazel target and associating the module with its label
+			// 2) Automatically generating with bp2build converters
+			//
+			// bp2build converters are used for the majority of modules.
 			if b, ok := m.(android.Bazelable); ok && b.HasHandcraftedLabel() {
-				metrics.handCraftedTargetCount += 1
-				metrics.TotalModuleCount += 1
-				metrics.AddConvertedModule(m.Name())
+				// Handle modules converted to handcrafted targets.
+				//
+				// Since these modules are associated with some handcrafted
+				// target in a BUILD file, we simply append the entire contents
+				// of that BUILD file to the generated BUILD file.
+				//
+				// The append operation is only done once, even if there are
+				// multiple modules from the same directory associated to
+				// targets in the same BUILD file (or package).
+
+				// Log the module.
+				metrics.AddConvertedModule(m.Name(), Handcrafted)
+
 				pathToBuildFile := getBazelPackagePath(b)
-				// We are using the entire contents of handcrafted build file, so if multiple targets within
-				// a package have handcrafted targets, we only want to include the contents one time.
 				if _, exists := buildFileToAppend[pathToBuildFile]; exists {
+					// Append the BUILD file content once per package, at most.
 					return
 				}
 				t, err := getHandcraftedBuildContent(ctx, b, pathToBuildFile)
@@ -297,23 +311,29 @@
 				// something more targeted based on the rule type and target
 				buildFileToAppend[pathToBuildFile] = true
 			} else if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
+				// Handle modules converted to generated targets.
+
+				// Log the module.
+				metrics.AddConvertedModule(m.Name(), Generated)
+
+				// Handle modules with unconverted deps. By default, emit a warning.
 				if unconvertedDeps := aModule.GetUnconvertedBp2buildDeps(); len(unconvertedDeps) > 0 {
 					msg := fmt.Sprintf("%q depends on unconverted modules: %s", m.Name(), strings.Join(unconvertedDeps, ", "))
 					if ctx.unconvertedDepMode == warnUnconvertedDeps {
 						metrics.moduleWithUnconvertedDepsMsgs = append(metrics.moduleWithUnconvertedDepsMsgs, msg)
 					} else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps {
-						metrics.TotalModuleCount += 1
 						errs = append(errs, fmt.Errorf(msg))
 						return
 					}
 				}
 				targets = generateBazelTargets(bpCtx, aModule)
-				metrics.AddConvertedModule(m.Name())
 				for _, t := range targets {
-					metrics.RuleClassCount[t.ruleClass] += 1
+					// A module can potentially generate more than 1 Bazel
+					// target, each of a different rule class.
+					metrics.IncrementRuleClassCount(t.ruleClass)
 				}
 			} else {
-				metrics.TotalModuleCount += 1
+				metrics.IncrementUnconvertedCount()
 				return
 			}
 		case QueryView:
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 371593b..b3a1053 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -1022,18 +1022,18 @@
     }),
     implementation_deps = select({
         "//build/bazel/platforms/arch:arm": [],
-        "//conditions:default": [":arm_static_lib_excludes"],
+        "//conditions:default": [":arm_static_lib_excludes_bp2build_cc_library_static"],
     }) + select({
         "//build/bazel/product_variables:malloc_not_svelte": [],
-        "//conditions:default": [":malloc_not_svelte_static_lib_excludes"],
+        "//conditions:default": [":malloc_not_svelte_static_lib_excludes_bp2build_cc_library_static"],
     }),
     srcs_c = ["common.c"],
     whole_archive_deps = select({
         "//build/bazel/platforms/arch:arm": [],
-        "//conditions:default": [":arm_whole_static_lib_excludes"],
+        "//conditions:default": [":arm_whole_static_lib_excludes_bp2build_cc_library_static"],
     }) + select({
-        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib"],
-        "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes"],
+        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib_bp2build_cc_library_static"],
+        "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes_bp2build_cc_library_static"],
     }),
 )`,
 		},
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 9e7b3b6..1cc4143 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -9,14 +9,17 @@
 // Simple metrics struct to collect information about a Blueprint to BUILD
 // conversion process.
 type CodegenMetrics struct {
-	// Total number of Soong/Blueprint modules
-	TotalModuleCount int
+	// Total number of Soong modules converted to generated targets
+	generatedModuleCount int
+
+	// Total number of Soong modules converted to handcrafted targets
+	handCraftedModuleCount int
+
+	// Total number of unconverted Soong modules
+	unconvertedModuleCount int
 
 	// Counts of generated Bazel targets per Bazel rule class
-	RuleClassCount map[string]int
-
-	// Total number of handcrafted targets
-	handCraftedTargetCount int
+	ruleClassCount map[string]int
 
 	moduleWithUnconvertedDepsMsgs []string
 
@@ -26,22 +29,49 @@
 // Print the codegen metrics to stdout.
 func (metrics *CodegenMetrics) Print() {
 	generatedTargetCount := 0
-	for _, ruleClass := range android.SortedStringKeys(metrics.RuleClassCount) {
-		count := metrics.RuleClassCount[ruleClass]
+	for _, ruleClass := range android.SortedStringKeys(metrics.ruleClassCount) {
+		count := metrics.ruleClassCount[ruleClass]
 		fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count)
 		generatedTargetCount += count
 	}
 	fmt.Printf(
 		"[bp2build] Generated %d total BUILD targets and included %d handcrafted BUILD targets from %d Android.bp modules.\n With %d modules with unconverted deps \n\t%s",
 		generatedTargetCount,
-		metrics.handCraftedTargetCount,
-		metrics.TotalModuleCount,
+		metrics.handCraftedModuleCount,
+		metrics.TotalModuleCount(),
 		len(metrics.moduleWithUnconvertedDepsMsgs),
 		strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"))
 }
 
-func (metrics *CodegenMetrics) AddConvertedModule(moduleName string) {
+func (metrics *CodegenMetrics) IncrementRuleClassCount(ruleClass string) {
+	metrics.ruleClassCount[ruleClass] += 1
+}
+
+func (metrics *CodegenMetrics) IncrementUnconvertedCount() {
+	metrics.unconvertedModuleCount += 1
+}
+
+func (metrics *CodegenMetrics) TotalModuleCount() int {
+	return metrics.handCraftedModuleCount +
+		metrics.generatedModuleCount +
+		metrics.unconvertedModuleCount
+}
+
+type ConversionType int
+
+const (
+	Generated ConversionType = iota
+	Handcrafted
+)
+
+func (metrics *CodegenMetrics) AddConvertedModule(moduleName string, conversionType ConversionType) {
 	// Undo prebuilt_ module name prefix modifications
 	moduleName = android.RemoveOptionalPrebuiltPrefix(moduleName)
 	metrics.convertedModules = append(metrics.convertedModules, moduleName)
+
+	if conversionType == Handcrafted {
+		metrics.handCraftedModuleCount += 1
+	} else if conversionType == Generated {
+		metrics.generatedModuleCount += 1
+	}
 }
diff --git a/cc/Android.bp b/cc/Android.bp
index bff2761..190d55e 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -63,6 +63,7 @@
         "library.go",
         "library_headers.go",
         "library_sdk_member.go",
+        "native_bridge_sdk_trait.go",
         "object.go",
         "test.go",
         "toolchain_library.go",
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 67ea70e..e48f757 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -20,6 +20,7 @@
 
 	"android/soong/android"
 	"android/soong/bazel"
+	"github.com/google/blueprint"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -136,10 +137,10 @@
 	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
 		attrs.Copts.SetSelectValue(axis, config, props.Cflags)
 		attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
-		attrs.Static_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
-		attrs.Dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
-		attrs.Whole_archive_deps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs))
-		attrs.System_dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.System_shared_libs))
+		attrs.Static_deps.SetSelectValue(axis, config, bazelLabelForStaticDeps(ctx, props.Static_libs))
+		attrs.Dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.Shared_libs))
+		attrs.Whole_archive_deps.SetSelectValue(axis, config, bazelLabelForWholeDeps(ctx, props.Whole_static_libs))
+		attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))
 	}
 	// system_dynamic_deps distinguishes between nil/empty list behavior:
 	//    nil -> use default values
@@ -388,9 +389,9 @@
 				// Excludes to parallel Soong:
 				// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0
 				staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs)
-				staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs))
+				staticDeps.SetSelectValue(axis, config, bazelLabelForStaticDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs))
 				wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
-				wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
+				wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
 
 				systemSharedLibs := baseLinkerProps.System_shared_libs
 				// systemSharedLibs distinguishes between nil/empty list behavior:
@@ -399,15 +400,15 @@
 				if len(systemSharedLibs) > 0 {
 					systemSharedLibs = android.FirstUniqueStrings(systemSharedLibs)
 				}
-				systemSharedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, systemSharedLibs))
+				systemSharedDeps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, systemSharedLibs))
 
 				sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
-				dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
+				dynamicDeps.SetSelectValue(axis, config, bazelLabelForSharedDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
 
 				headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
-				headerDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, headerLibs))
+				headerDeps.SetSelectValue(axis, config, bazelLabelForHeaderDeps(ctx, headerLibs))
 				exportedLibs := android.FirstUniqueStrings(baseLinkerProps.Export_header_lib_headers)
-				exportedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, exportedLibs))
+				exportedDeps.SetSelectValue(axis, config, bazelLabelForHeaderDeps(ctx, exportedLibs))
 
 				linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps))
 				if baseLinkerProps.Version_script != nil {
@@ -424,14 +425,14 @@
 		// reference to the bazel attribute that should be set for the given product variable config
 		attribute *bazel.LabelListAttribute
 
-		depResolutionFunc func(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList
+		depResolutionFunc func(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList
 	}
 
 	productVarToDepFields := map[string]productVarDep{
 		// product variables do not support exclude_shared_libs
-		"Shared_libs":       productVarDep{attribute: &dynamicDeps, depResolutionFunc: android.BazelLabelForModuleDepsExcludes},
-		"Static_libs":       productVarDep{"Exclude_static_libs", &staticDeps, android.BazelLabelForModuleDepsExcludes},
-		"Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps, android.BazelLabelForModuleWholeDepsExcludes},
+		"Shared_libs":       productVarDep{attribute: &dynamicDeps, depResolutionFunc: bazelLabelForSharedDepsExcludes},
+		"Static_libs":       productVarDep{"Exclude_static_libs", &staticDeps, bazelLabelForStaticDepsExcludes},
+		"Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps, bazelLabelForWholeDepsExcludes},
 	}
 
 	productVariableProps := android.ProductVariableProperties(ctx)
@@ -559,3 +560,59 @@
 
 	return exported
 }
+
+func bazelLabelForStaticModule(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+	label := android.BazelModuleLabel(ctx, m)
+	if aModule, ok := m.(android.Module); ok {
+		if ctx.OtherModuleType(aModule) == "cc_library" && !android.GenerateCcLibraryStaticOnly(m.Name()) {
+			label += "_bp2build_cc_library_static"
+		}
+	}
+	return label
+}
+
+func bazelLabelForSharedModule(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+	// cc_library, at it's root name, propagates the shared library, which depends on the static
+	// library.
+	return android.BazelModuleLabel(ctx, m)
+}
+
+func bazelLabelForStaticWholeModuleDeps(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+	label := bazelLabelForStaticModule(ctx, m)
+	if aModule, ok := m.(android.Module); ok {
+		if android.IsModulePrebuilt(aModule) {
+			label += "_alwayslink"
+		}
+	}
+	return label
+}
+
+func bazelLabelForWholeDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticWholeModuleDeps)
+}
+
+func bazelLabelForWholeDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticWholeModuleDeps)
+}
+
+func bazelLabelForStaticDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticModule)
+}
+
+func bazelLabelForStaticDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticModule)
+}
+
+func bazelLabelForSharedDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForSharedModule)
+}
+
+func bazelLabelForHeaderDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+	// This is not elegant, but bp2build's shared library targets only propagate
+	// their header information as part of the normal C++ provider.
+	return bazelLabelForSharedDeps(ctx, modules)
+}
+
+func bazelLabelForSharedDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
+}
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 8c678a1..e72efae 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -47,15 +47,29 @@
 	"android.hardware.power-V1-ndk",
 	"android.hardware.power-V1-ndk_platform",
 	"android.hardware.power-ndk_platform",
-	"android.hardware.rebootescrow-V1-ndk",
-	"android.hardware.rebootescrow-V1-ndk_platform",
 	"android.hardware.power.stats-V1-ndk",
 	"android.hardware.power.stats-V1-ndk_platform",
 	"android.hardware.power.stats-ndk_platform",
 	"android.hardware.power.stats-unstable-ndk_platform",
+	"android.hardware.rebootescrow-V1-ndk",
+	"android.hardware.rebootescrow-V1-ndk_platform",
+	"android.hardware.rebootescrow-ndk_platform",
 	"android.hardware.radio-V1-ndk",
 	"android.hardware.radio-V1-ndk_platform",
-	"android.hardware.rebootescrow-ndk_platform",
+	"android.hardware.radio.config-V1-ndk",
+	"android.hardware.radio.config-V1-ndk_platform",
+	"android.hardware.radio.data-V1-ndk",
+	"android.hardware.radio.data-V1-ndk_platform",
+	"android.hardware.radio.messaging-V1-ndk",
+	"android.hardware.radio.messaging-V1-ndk_platform",
+	"android.hardware.radio.modem-V1-ndk",
+	"android.hardware.radio.modem-V1-ndk_platform",
+	"android.hardware.radio.network-V1-ndk",
+	"android.hardware.radio.network-V1-ndk_platform",
+	"android.hardware.radio.sim-V1-ndk",
+	"android.hardware.radio.sim-V1-ndk_platform",
+	"android.hardware.radio.voice-V1-ndk",
+	"android.hardware.radio.voice-V1-ndk_platform",
 	"android.hardware.security.keymint-V1-ndk",
 	"android.hardware.security.keymint-V1-ndk_platform",
 	"android.hardware.security.keymint-ndk_platform",
diff --git a/cc/library.go b/cc/library.go
index f5a82c2..8a572f9 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -277,7 +277,7 @@
 	// For some cc_library modules, their static variants are ready to be
 	// converted, but not their shared variants. For these modules, delegate to
 	// the cc_library_static bp2build converter temporarily instead.
-	if android.GenerateCcLibraryStaticOnly(ctx) {
+	if android.GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
 		ccSharedOrStaticBp2BuildMutatorInternal(ctx, m, "cc_library_static")
 		return
 	}
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 14b90c1..b335035 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -33,6 +33,9 @@
 		PropertyName:    "native_header_libs",
 		SupportsSdk:     true,
 		HostOsDependent: true,
+		Traits: []android.SdkMemberTrait{
+			nativeBridgeSdkTrait,
+		},
 	},
 	prebuiltModuleType: "cc_prebuilt_library_headers",
 	noOutputFiles:      true,
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 559e940..bed7954 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -75,8 +75,33 @@
 }
 
 func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
-	targets := ctx.MultiTargets()
+	// The base set of targets which does not include native bridge targets.
+	defaultTargets := ctx.MultiTargets()
+
+	// The lazily created list of native bridge targets.
+	var includeNativeBridgeTargets []android.Target
+
 	for _, lib := range names {
+		targets := defaultTargets
+
+		// If native bridge support is required in the sdk snapshot then add native bridge targets to
+		// the basic list of targets that are required.
+		nativeBridgeSupport := ctx.RequiresTrait(lib, nativeBridgeSdkTrait)
+		if nativeBridgeSupport && ctx.Device() {
+			// If not already computed then compute the list of native bridge targets.
+			if includeNativeBridgeTargets == nil {
+				includeNativeBridgeTargets = append([]android.Target{}, defaultTargets...)
+				allAndroidTargets := ctx.Config().Targets[android.Android]
+				for _, possibleNativeBridgeTarget := range allAndroidTargets {
+					if possibleNativeBridgeTarget.NativeBridge == android.NativeBridgeEnabled {
+						includeNativeBridgeTargets = append(includeNativeBridgeTargets, possibleNativeBridgeTarget)
+					}
+				}
+			}
+
+			// Include the native bridge targets as well.
+			targets = includeNativeBridgeTargets
+		}
 		for _, target := range targets {
 			name, version := StubsLibNameAndVersion(lib)
 			if version == "" {
@@ -122,6 +147,10 @@
 
 	ccModule := member.Variants()[0].(*Module)
 
+	if ctx.RequiresTrait(nativeBridgeSdkTrait) {
+		pbm.AddProperty("native_bridge_supported", true)
+	}
+
 	if proptools.Bool(ccModule.Properties.Recovery_available) {
 		pbm.AddProperty("recovery_available", true)
 	}
@@ -436,7 +465,11 @@
 	exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate(
 		exportedInfo.IncludeDirs, isGeneratedHeaderDirectory)
 
-	p.archSubDir = ccModule.Target().Arch.ArchType.String()
+	target := ccModule.Target()
+	p.archSubDir = target.Arch.ArchType.String()
+	if target.NativeBridge == android.NativeBridgeEnabled {
+		p.archSubDir += "_native_bridge"
+	}
 
 	// Make sure that the include directories are unique.
 	p.ExportedIncludeDirs = android.FirstUniquePaths(exportedIncludeDirs)
diff --git a/cc/native_bridge_sdk_trait.go b/cc/native_bridge_sdk_trait.go
new file mode 100644
index 0000000..1326d57
--- /dev/null
+++ b/cc/native_bridge_sdk_trait.go
@@ -0,0 +1,33 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import "android/soong/android"
+
+// This file contains support for the native bridge sdk trait.
+
+func init() {
+	android.RegisterSdkMemberTrait(nativeBridgeSdkTrait)
+}
+
+type nativeBridgeSdkTraitStruct struct {
+	android.SdkMemberTraitBase
+}
+
+var nativeBridgeSdkTrait android.SdkMemberTrait = &nativeBridgeSdkTraitStruct{
+	SdkMemberTraitBase: android.SdkMemberTraitBase{
+		PropertyName: "native_bridge_support",
+	},
+}
diff --git a/cc/test.go b/cc/test.go
index 3934784..047a69e 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -357,7 +357,8 @@
 }
 
 func (test *testBinary) install(ctx ModuleContext, file android.Path) {
-	testInstallBase := "/data/local/tests/unrestricted"
+	// TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base.
+	testInstallBase := "/data/local/tmp"
 	if ctx.inVendor() || ctx.useVndk() {
 		testInstallBase = "/data/local/tests/vendor"
 	}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index fa63b46..3c9cac1 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -218,10 +218,16 @@
 	}
 }
 
+func forceAnsiOutput() bool {
+	value := os.Getenv("SOONG_UI_ANSI_OUTPUT")
+	return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true"
+}
+
 func main() {
 	stdio := terminal.StdioImpl{}
 
-	output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false)
+	output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false,
+		forceAnsiOutput())
 	log := logger.New(output)
 	defer log.Cleanup()
 
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index fe567a9..be81487 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -24,6 +24,7 @@
 	"io/ioutil"
 	"os"
 	"os/exec"
+	"path"
 	"path/filepath"
 	"regexp"
 	"sort"
@@ -164,7 +165,8 @@
 type Dependency struct {
 	XMLName xml.Name `xml:"dependency"`
 
-	BpTarget string `xml:"-"`
+	BpTarget    string `xml:"-"`
+	BazelTarget string `xml:"-"`
 
 	GroupId    string `xml:"groupId"`
 	ArtifactId string `xml:"artifactId"`
@@ -230,6 +232,14 @@
 	}
 }
 
+func (p Pom) BazelTargetType() string {
+	if p.IsAar() {
+		return "android_library"
+	} else {
+		return "java_library"
+	}
+}
+
 func (p Pom) ImportModuleType() string {
 	if p.IsAar() {
 		return "android_library_import"
@@ -240,6 +250,14 @@
 	}
 }
 
+func (p Pom) BazelImportTargetType() string {
+	if p.IsAar() {
+		return "aar_import"
+	} else {
+		return "java_import"
+	}
+}
+
 func (p Pom) ImportProperty() string {
 	if p.IsAar() {
 		return "aars"
@@ -248,6 +266,14 @@
 	}
 }
 
+func (p Pom) BazelImportProperty() string {
+	if p.IsAar() {
+		return "aar"
+	} else {
+		return "jars"
+	}
+}
+
 func (p Pom) BpName() string {
 	if p.BpTarget == "" {
 		p.BpTarget = rewriteNames.MavenToBp(p.GroupId, p.ArtifactId)
@@ -263,6 +289,14 @@
 	return p.BpDeps("aar", []string{"compile", "runtime"})
 }
 
+func (p Pom) BazelJarDeps() []string {
+	return p.BazelDeps("jar", []string{"compile", "runtime"})
+}
+
+func (p Pom) BazelAarDeps() []string {
+	return p.BazelDeps("aar", []string{"compile", "runtime"})
+}
+
 func (p Pom) BpExtraStaticLibs() []string {
 	return extraStaticLibs[p.BpName()]
 }
@@ -289,6 +323,91 @@
 	return ret
 }
 
+// BazelDeps obtains dependencies filtered by type and scope. The results of this
+// method are formatted as Bazel BUILD targets.
+func (p Pom) BazelDeps(typeExt string, scopes []string) []string {
+	var ret []string
+	for _, d := range p.Dependencies {
+		if d.Type != typeExt || !InList(d.Scope, scopes) {
+			continue
+		}
+		ret = append(ret, d.BazelTarget)
+	}
+	return ret
+}
+
+func PathModVars() (string, string, string) {
+	cmd := "/bin/bash"
+	androidTop := os.Getenv("ANDROID_BUILD_TOP")
+	envSetupSh := path.Join(androidTop, "build/envsetup.sh")
+	return cmd, androidTop, envSetupSh
+}
+
+func InitRefreshMod(poms []*Pom) error {
+	cmd, _, envSetupSh := PathModVars()
+	// refreshmod is expensive, so if pathmod is already working we can skip it.
+	_, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && pathmod "+poms[0].BpName()).Output()
+	if exitErr, _ := err.(*exec.ExitError); exitErr != nil || err != nil {
+		_, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && refreshmod").Output()
+		if exitErr, _ := err.(*exec.ExitError); exitErr != nil {
+			return fmt.Errorf("failed to run %s\n%s\ntry running lunch.", cmd, string(exitErr.Stderr))
+		} else if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func BazelifyExtraDeps(extraDeps ExtraDeps, modules map[string]*Pom) error {
+	for _, deps := range extraDeps {
+		for _, dep := range deps {
+			bazelName, err := BpNameToBazelTarget(dep, modules)
+			if err != nil {
+				return err
+			}
+			dep = bazelName
+		}
+
+	}
+	return nil
+}
+
+func (p *Pom) GetBazelDepNames(modules map[string]*Pom) error {
+	for _, d := range p.Dependencies {
+		bazelName, err := BpNameToBazelTarget(d.BpName(), modules)
+		if err != nil {
+			return err
+		}
+		d.BazelTarget = bazelName
+	}
+	return nil
+}
+
+func BpNameToBazelTarget(bpName string, modules map[string]*Pom) (string, error) {
+	cmd, androidTop, envSetupSh := PathModVars()
+
+	if _, ok := modules[bpName]; ok {
+		// We've seen the POM for this dependency, it will be local to the output BUILD file
+		return ":" + bpName, nil
+	} else {
+		// we don't have the POM for this artifact, find and use the fully qualified target name.
+		output, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && pathmod "+bpName).Output()
+		if exitErr, _ := err.(*exec.ExitError); exitErr != nil {
+			return "", fmt.Errorf("failed to run %s %s\n%s", cmd, bpName, string(exitErr.Stderr))
+		} else if err != nil {
+			return "", err
+		}
+		relPath := ""
+		for _, line := range strings.Fields(string(output)) {
+			if strings.Contains(line, androidTop) {
+				relPath = strings.TrimPrefix(line, androidTop)
+				relPath = strings.TrimLeft(relPath, "/")
+			}
+		}
+		return "//" + relPath + ":" + bpName, nil
+	}
+}
+
 func (p Pom) SdkVersion() string {
 	return sdkVersion
 }
@@ -512,6 +631,75 @@
 }
 `))
 
+var bazelTemplate = template.Must(template.New("bp").Parse(`
+{{.BazelImportTargetType}} (
+    name = "{{.BpName}}",
+    {{.BazelImportProperty}}: {{- if not .IsAar}}[{{- end}}"{{.ArtifactFile}}"{{- if not .IsAar}}]{{- end}},
+    visibility = ["//visibility:public"],
+    {{- if .IsAar}}
+    deps = [
+        {{- range .BazelJarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BazelAarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraStaticLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpOptionalUsesLibs}}
+        "{{.}}",
+        {{- end}}
+    ],
+    {{- end}}
+)
+`))
+
+var bazelDepsTemplate = template.Must(template.New("bp").Parse(`
+{{.BazelImportTargetType}} (
+    name = "{{.BpName}}",
+    {{.BazelImportProperty}} = {{- if not .IsAar}}[{{- end}}"{{.ArtifactFile}}"{{- if not .IsAar}}]{{- end}},
+    visibility = ["//visibility:public"],
+    deps = [
+        {{- range .BazelJarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BazelAarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraStaticLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpOptionalUsesLibs}}
+        "{{.}}",
+        {{- end}}
+    ],
+    exports = [
+        {{- range .BazelJarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BazelAarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraStaticLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpOptionalUsesLibs}}
+        "{{.}}",
+        {{- end}}
+    ],
+)
+`))
+
 func parse(filename string) (*Pom, error) {
 	data, err := ioutil.ReadFile(filename)
 	if err != nil {
@@ -559,12 +747,14 @@
 
 	// Extract the old args from the file
 	line := scanner.Text()
-	if strings.HasPrefix(line, "// pom2bp ") {
+	if strings.HasPrefix(line, "// pom2bp ") { // .bp file
 		line = strings.TrimPrefix(line, "// pom2bp ")
-	} else if strings.HasPrefix(line, "// pom2mk ") {
+	} else if strings.HasPrefix(line, "// pom2mk ") { // .bp file converted from .mk file
 		line = strings.TrimPrefix(line, "// pom2mk ")
-	} else if strings.HasPrefix(line, "# pom2mk ") {
+	} else if strings.HasPrefix(line, "# pom2mk ") { // .mk file
 		line = strings.TrimPrefix(line, "# pom2mk ")
+	} else if strings.HasPrefix(line, "# pom2bp ") { // Bazel BUILD file
+		line = strings.TrimPrefix(line, "# pom2bp ")
 	} else {
 		return fmt.Errorf("unexpected second line: %q", line)
 	}
@@ -650,6 +840,7 @@
 	}
 
 	var regen string
+	var pom2build bool
 
 	flag.Var(&excludes, "exclude", "Exclude module")
 	flag.Var(&extraStaticLibs, "extra-static-libs", "Extra static dependencies needed when depending on a module")
@@ -664,6 +855,7 @@
 	flag.BoolVar(&staticDeps, "static-deps", false, "Statically include direct dependencies")
 	flag.BoolVar(&jetifier, "jetifier", false, "Sets jetifier: true on all modules")
 	flag.StringVar(&regen, "regen", "", "Rewrite specified file")
+	flag.BoolVar(&pom2build, "pom2build", false, "If true, will generate a Bazel BUILD file *instead* of a .bp file")
 	flag.Parse()
 
 	if regen != "" {
@@ -758,6 +950,16 @@
 		os.Exit(1)
 	}
 
+	if pom2build {
+		if err := InitRefreshMod(poms); err != nil {
+			fmt.Fprintf(os.Stderr, "Error in refreshmod: %s", err)
+			os.Exit(1)
+		}
+		BazelifyExtraDeps(extraStaticLibs, modules)
+		BazelifyExtraDeps(extraLibs, modules)
+		BazelifyExtraDeps(optionalUsesLibs, modules)
+	}
+
 	for _, pom := range poms {
 		if pom.IsAar() {
 			err := pom.ExtractMinSdkVersion()
@@ -767,19 +969,32 @@
 			}
 		}
 		pom.FixDeps(modules)
+		if pom2build {
+			pom.GetBazelDepNames(modules)
+		}
 	}
 
 	buf := &bytes.Buffer{}
+	commentString := "//"
+	if pom2build {
+		commentString = "#"
+	}
+	fmt.Fprintln(buf, commentString, "Automatically generated with:")
+	fmt.Fprintln(buf, commentString, "pom2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " "))
 
-	fmt.Fprintln(buf, "// Automatically generated with:")
-	fmt.Fprintln(buf, "// pom2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " "))
+	depsTemplate := bpDepsTemplate
+	template := bpTemplate
+	if pom2build {
+		depsTemplate = bazelDepsTemplate
+		template = bazelTemplate
+	}
 
 	for _, pom := range poms {
 		var err error
 		if staticDeps {
-			err = bpDepsTemplate.Execute(buf, pom)
+			err = depsTemplate.Execute(buf, pom)
 		} else {
-			err = bpTemplate.Execute(buf, pom)
+			err = template.Execute(buf, pom)
 		}
 		if err != nil {
 			fmt.Fprintln(os.Stderr, "Error writing", pom.PomFile, pom.BpName(), err)
@@ -787,11 +1002,15 @@
 		}
 	}
 
-	out, err := bpfix.Reformat(buf.String())
-	if err != nil {
-		fmt.Fprintln(os.Stderr, "Error formatting output", err)
-		os.Exit(1)
+	if pom2build {
+		os.Stdout.WriteString(buf.String())
+	} else {
+		out, err := bpfix.Reformat(buf.String())
+		if err != nil {
+			fmt.Fprintln(os.Stderr, "Error formatting output", err)
+			os.Exit(1)
+		}
+		os.Stdout.WriteString(out)
 	}
 
-	os.Stdout.WriteString(out)
 }
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 703a875..e85163e 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -22,6 +22,7 @@
         "blueprint",
         "blueprint-bootstrap",
         "golang-protobuf-proto",
+        "golang-protobuf-android",
         "soong",
         "soong-android",
         "soong-bp2build",
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 09a2234..c5e8896 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -23,14 +23,14 @@
 	"strings"
 	"time"
 
+	"android/soong/android"
 	"android/soong/bp2build"
 	"android/soong/shared"
 
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/deptools"
 	"github.com/google/blueprint/pathtools"
-
-	"android/soong/android"
+	androidProtobuf "google.golang.org/protobuf/android"
 )
 
 var (
@@ -85,6 +85,12 @@
 	// Flags that probably shouldn't be flags of soong_build but we haven't found
 	// the time to remove them yet
 	flag.BoolVar(&runGoTests, "t", false, "build and run go tests during bootstrap")
+
+	// Disable deterministic randomization in the protobuf package, so incremental
+	// builds with unrelated Soong changes don't trigger large rebuilds (since we
+	// write out text protos in command lines, and command line changes trigger
+	// rebuilds).
+	androidProtobuf.DisableRand()
 }
 
 func newNameResolver(config android.Config) *android.NameResolver {
diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go
index 8d8f37f..d2fbed4 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -372,6 +372,7 @@
     {{if .Properties -}}
       <div class="accordion"  id="{{getModule}}.{{.Name}}">
         <span class="fixed">&#x2295</span><b>{{.Name}}</b>
+        <i>{{.Type}}</i>
         {{- range .OtherNames -}}, {{.}}{{- end -}}
       </div>
       <div class="collapsible">
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index d709787..9ee373e 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -164,7 +164,8 @@
 
 	// Create a terminal output that mimics Ninja's.
 	output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
-		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
+		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
+		build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
 
 	// Attach a new logger instance to the terminal output.
 	log := logger.New(output)
diff --git a/cuj/cuj.go b/cuj/cuj.go
index 413f423..869e0f7 100644
--- a/cuj/cuj.go
+++ b/cuj/cuj.go
@@ -48,7 +48,7 @@
 
 // Run runs a single build command.  It emulates the "m" command line by calling into Soong UI directly.
 func (t *Test) Run(logsDir string) {
-	output := terminal.NewStatusOutput(os.Stdout, "", false, false)
+	output := terminal.NewStatusOutput(os.Stdout, "", false, false, false)
 
 	log := logger.New(output)
 	defer log.Cleanup()
@@ -138,6 +138,8 @@
 
 	cujDir := filepath.Join(outDir, "cuj_tests")
 
+	wd, _ := os.Getwd()
+	os.Setenv("TOP", wd)
 	// Use a subdirectory for the out directory for the tests to keep them isolated.
 	os.Setenv("OUT_DIR", filepath.Join(cujDir, "out"))
 
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 7733c1b..965b755 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -261,10 +261,18 @@
 			clcTarget = append(clcTarget, GetSystemServerDexLocation(global, lib))
 		}
 
-		// Copy the system server jar to a predefined location where dex2oat will find it.
-		dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
-		rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
-		rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost)
+		if DexpreoptRunningInSoong {
+			// Copy the system server jar to a predefined location where dex2oat will find it.
+			dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
+			rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
+			rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost)
+		} else {
+			// For Make modules the copy rule is generated in the makefiles, not in dexpreopt.sh.
+			// This is necessary to expose the rule to Ninja, otherwise it has rules that depend on
+			// the jar (namely, dexpreopt commands for all subsequent system server jars that have
+			// this one in their class loader context), but no rule that creates it (because Ninja
+			// cannot see the rule in the generated dexpreopt.sh script).
+		}
 
 		checkSystemServerOrder(ctx, jarIndex)
 
diff --git a/java/droidstubs.go b/java/droidstubs.go
index ec1b04a..7fd88fc 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -156,6 +156,7 @@
 
 // Provider of information about API stubs, used by java_sdk_library.
 type ApiStubsProvider interface {
+	AnnotationsZip() android.Path
 	ApiFilePath
 	RemovedApiFilePath() android.Path
 
@@ -210,6 +211,10 @@
 	}
 }
 
+func (d *Droidstubs) AnnotationsZip() android.Path {
+	return d.annotationsZip
+}
+
 func (d *Droidstubs) ApiFilePath() android.Path {
 	return d.apiFilePath
 }
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 51cd501..1c6fbac 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -1194,13 +1194,6 @@
 // deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by
 // Soong but should instead only be reported in ninja if the file is actually built.
 func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.Module) bool {
-	// TODO(b/179354495): Remove this workaround when it is unnecessary.
-	// Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So,
-	// create a fake one that will cause a build error only if it is used.
-	if ctx.Config().AlwaysUsePrebuiltSdks() {
-		return true
-	}
-
 	// Any missing dependency should be allowed.
 	if ctx.Config().AllowMissingDependencies() {
 		return true
diff --git a/java/java.go b/java/java.go
index 2ca4ac8..4a44866 100644
--- a/java/java.go
+++ b/java/java.go
@@ -533,6 +533,10 @@
 		j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			j.Stem()+".jar", j.outputFile, extraInstallDeps...)
 	}
+
+	if ctx.Windows() {
+		j.HideFromMake()
+	}
 }
 
 func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -1030,14 +1034,14 @@
 
 type binaryProperties struct {
 	// installable script to execute the resulting jar
-	Wrapper *string `android:"path"`
+	Wrapper *string `android:"path,arch_variant"`
 
 	// Name of the class containing main to be inserted into the manifest as Main-Class.
 	Main_class *string
 
 	// Names of modules containing JNI libraries that should be installed alongside the host
 	// variant of the binary.
-	Jni_libs []string
+	Jni_libs []string `android:"arch_variant"`
 }
 
 type Binary struct {
@@ -1075,14 +1079,27 @@
 		if j.binaryProperties.Wrapper != nil {
 			j.wrapperFile = android.PathForModuleSrc(ctx, *j.binaryProperties.Wrapper)
 		} else {
+			if ctx.Windows() {
+				ctx.PropertyErrorf("wrapper", "wrapper is required for Windows")
+			}
+
 			j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh")
 		}
 
+		ext := ""
+		if ctx.Windows() {
+			ext = ".bat"
+		}
+
 		// The host installation rules make the installed wrapper depend on all the dependencies
 		// of the wrapper variant, which will include the common variant's jar file and any JNI
 		// libraries.  This is verified by TestBinary.
 		j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"),
-			ctx.ModuleName(), j.wrapperFile)
+			ctx.ModuleName()+ext, j.wrapperFile)
+	}
+
+	if ctx.Windows() {
+		j.HideFromMake()
 	}
 }
 
@@ -1283,6 +1300,10 @@
 		j.hideApexVariantFromMake = true
 	}
 
+	if ctx.Windows() {
+		j.HideFromMake()
+	}
+
 	jars := android.PathsForModuleSrc(ctx, j.properties.Jars)
 
 	jarName := j.Stem() + ".jar"
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 2d8aef7..d469522 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -550,6 +550,9 @@
 
 	// The stubs source jar.
 	stubsSrcJar android.OptionalPath
+
+	// Extracted annotations.
+	annotationsZip android.OptionalPath
 }
 
 func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error {
@@ -585,6 +588,7 @@
 }
 
 func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider) {
+	paths.annotationsZip = android.OptionalPathForPath(provider.AnnotationsZip())
 	paths.currentApiFilePath = android.OptionalPathForPath(provider.ApiFilePath())
 	paths.removedApiFilePath = android.OptionalPathForPath(provider.RemovedApiFilePath())
 }
@@ -739,6 +743,8 @@
 	apiTxtComponentName = "api.txt"
 
 	removedApiTxtComponentName = "removed-api.txt"
+
+	annotationsComponentName = "annotations.zip"
 )
 
 // A regular expression to match tags that reference a specific stubs component.
@@ -757,7 +763,7 @@
 	scopesRegexp := choice(allScopeNames...)
 
 	// Regular expression to match one of the components.
-	componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName)
+	componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName, annotationsComponentName)
 
 	// Regular expression to match any combination of one scope and one component.
 	return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp))
@@ -765,9 +771,7 @@
 
 // For OutputFileProducer interface
 //
-// .<scope>.stubs.source
-// .<scope>.api.txt
-// .<scope>.removed-api.txt
+// .<scope>.<component name>, for all ComponentNames (for example: .public.removed-api.txt)
 func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Paths, error) {
 	if groups := tagSplitter.FindStringSubmatch(tag); groups != nil {
 		scopeName := groups[1]
@@ -794,6 +798,11 @@
 				if paths.removedApiFilePath.Valid() {
 					return android.Paths{paths.removedApiFilePath.Path()}, nil
 				}
+
+			case annotationsComponentName:
+				if paths.annotationsZip.Valid() {
+					return android.Paths{paths.annotationsZip.Path()}, nil
+				}
 			}
 
 			return nil, fmt.Errorf("%s not available for api scope %s", component, scopeName)
@@ -1888,6 +1897,9 @@
 
 	// The removed.txt
 	Removed_api *string `android:"path"`
+
+	// Annotation zip
+	Annotations *string `android:"path"`
 }
 
 type sdkLibraryImportProperties struct {
@@ -2201,6 +2213,7 @@
 		}
 
 		paths := module.getScopePathsCreateIfNeeded(apiScope)
+		paths.annotationsZip = android.OptionalPathForModuleSrc(ctx, scopeProperties.Annotations)
 		paths.currentApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Current_api)
 		paths.removedApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Removed_api)
 	}
@@ -2551,6 +2564,7 @@
 	StubsSrcJar    android.Path
 	CurrentApiFile android.Path
 	RemovedApiFile android.Path
+	AnnotationsZip android.Path
 	SdkVersion     string
 }
 
@@ -2576,6 +2590,10 @@
 			if paths.removedApiFilePath.Valid() {
 				properties.RemovedApiFile = paths.removedApiFilePath.Path()
 			}
+			// The annotations zip is only available for modules that set annotations_enabled: true.
+			if paths.annotationsZip.Valid() {
+				properties.AnnotationsZip = paths.annotationsZip.Path()
+			}
 			s.Scopes[apiScope] = properties
 		}
 	}
@@ -2640,6 +2658,12 @@
 				scopeSet.AddProperty("removed_api", removedApiSnapshotPath)
 			}
 
+			if properties.AnnotationsZip != nil {
+				annotationsSnapshotPath := filepath.Join(scopeDir, ctx.Name()+"_annotations.zip")
+				ctx.SnapshotBuilder().CopyToSnapshot(properties.AnnotationsZip, annotationsSnapshotPath)
+				scopeSet.AddProperty("annotations", annotationsSnapshotPath)
+			}
+
 			if properties.SdkVersion != "" {
 				scopeSet.AddProperty("sdk_version", properties.SdkVersion)
 			}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index d6c0946..be23536 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -248,7 +248,7 @@
 	}
 }
 
-func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) {
+func TestJavaSdkLibrary_AccessOutputFiles(t *testing.T) {
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -258,6 +258,31 @@
 			name: "foo",
 			srcs: ["a.java"],
 			api_packages: ["foo"],
+			annotations_enabled: true,
+			public: {
+				enabled: true,
+			},
+		}
+		java_library {
+			name: "bar",
+			srcs: ["b.java", ":foo{.public.stubs.source}"],
+			java_resources: [":foo{.public.annotations.zip}"],
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_AccessOutputFiles_NoAnnotations(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+	).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": path dependency ":foo{.public.annotations.zip}": annotations.zip not available for api scope public`)).
+		RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
 			public: {
 				enabled: true,
 			},
@@ -266,6 +291,7 @@
 		java_library {
 			name: "bar",
 			srcs: ["b.java", ":foo{.public.stubs.source}"],
+			java_resources: [":foo{.public.annotations.zip}"],
 		}
 		`)
 }
@@ -329,6 +355,7 @@
 				stub_srcs: ["a.java"],
 				current_api: "api/current.txt",
 				removed_api: "api/removed.txt",
+				annotations: "x/annotations.zip",
 			},
 		}
 
@@ -338,6 +365,7 @@
 			java_resources: [
 				":foo{.public.api.txt}",
 				":foo{.public.removed-api.txt}",
+				":foo{.public.annotations.zip}",
 			],
 		}
 		`)
diff --git a/java/testing.go b/java/testing.go
index a642753..99d55a0 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -300,6 +300,7 @@
 		"kotlin-stdlib-jdk7",
 		"kotlin-stdlib-jdk8",
 		"kotlin-annotations",
+		"stub-annotations",
 	}
 
 	for _, extra := range extraModules {
diff --git a/python/python.go b/python/python.go
index a35a1ac..f900172 100644
--- a/python/python.go
+++ b/python/python.go
@@ -45,7 +45,7 @@
 type VersionProperties struct {
 	// whether the module is required to be built with this version.
 	// Defaults to true for Python 3, and false otherwise.
-	Enabled *bool `android:"arch_variant"`
+	Enabled *bool
 
 	// list of source files specific to this Python version.
 	// Using the syntax ":module", srcs may reference the outputs of other modules that produce source files,
@@ -60,7 +60,7 @@
 	Libs []string `android:"arch_variant"`
 
 	// whether the binary is required to be built with embedded launcher for this version, defaults to false.
-	Embedded_launcher *bool `android:"arch_variant"` // TODO(b/174041232): Remove this property
+	Embedded_launcher *bool // TODO(b/174041232): Remove this property
 }
 
 // properties that apply to all python modules
@@ -70,10 +70,10 @@
 	// eg. Pkg_path = "a/b/c"; Other packages can reference this module by using
 	// (from a.b.c import ...) statement.
 	// if left unspecified, all the source/data files path is unchanged within zip file.
-	Pkg_path *string `android:"arch_variant"`
+	Pkg_path *string
 
 	// true, if the Python module is used internally, eg, Python std libs.
-	Is_internal *bool `android:"arch_variant"`
+	Is_internal *bool
 
 	// list of source (.py) files compatible both with Python2 and Python3 used to compile the
 	// Python module.
diff --git a/sdk/Android.bp b/sdk/Android.bp
index 0c9bf27..c6544d6 100644
--- a/sdk/Android.bp
+++ b/sdk/Android.bp
@@ -16,6 +16,7 @@
     srcs: [
         "bp.go",
         "exports.go",
+        "member_trait.go",
         "member_type.go",
         "sdk.go",
         "update.go",
@@ -28,6 +29,7 @@
         "exports_test.go",
         "java_sdk_test.go",
         "license_sdk_test.go",
+        "member_trait_test.go",
         "sdk_test.go",
         "testing.go",
     ],
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 25e35fc..da90c6d 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -32,6 +32,23 @@
 	"some/where/stubslib.map.txt":     nil,
 }
 
+// Adds a native bridge target to the configured list of targets.
+var prepareForTestWithNativeBridgeTarget = android.FixtureModifyConfig(func(config android.Config) {
+	config.Targets[android.Android] = append(config.Targets[android.Android], android.Target{
+		Os: android.Android,
+		Arch: android.Arch{
+			ArchType:     android.Arm64,
+			ArchVariant:  "armv8-a",
+			CpuVariant:   "cpu",
+			Abi:          nil,
+			ArchFeatures: nil,
+		},
+		NativeBridge:             android.NativeBridgeEnabled,
+		NativeBridgeHostArchName: "x86_64",
+		NativeBridgeRelativePath: "native_bridge",
+	})
+})
+
 func testSdkWithCc(t *testing.T, bp string) *android.TestResult {
 	t.Helper()
 	return testSdkWithFs(t, bp, ccTestFs)
@@ -1979,6 +1996,91 @@
 	)
 }
 
+func TestSnapshotWithCcHeadersLibraryAndNativeBridgeSupport(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		cc.PrepareForTestWithCcDefaultModules,
+		PrepareForTestWithSdkBuildComponents,
+		ccTestFs.AddToFixture(),
+		prepareForTestWithNativeBridgeTarget,
+	).RunTestWithBp(t, `
+		sdk {
+			name: "mysdk",
+			native_header_libs: ["mynativeheaders"],
+			traits: {
+				native_bridge_support: ["mynativeheaders"],
+			},
+		}
+
+		cc_library_headers {
+			name: "mynativeheaders",
+			export_include_dirs: ["myinclude"],
+			stl: "none",
+			system_shared_libs: [],
+			native_bridge_supported: true,
+		}
+	`)
+
+	CheckSnapshot(t, result, "mysdk", "",
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_headers {
+    name: "mynativeheaders",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    native_bridge_supported: true,
+    stl: "none",
+    compile_multilib: "both",
+    system_shared_libs: [],
+    export_include_dirs: ["include/myinclude"],
+}
+`),
+		checkAllCopyRules(`
+myinclude/Test.h -> include/myinclude/Test.h
+`),
+	)
+}
+
+// TestSnapshotWithCcHeadersLibrary_DetectsNativeBridgeSpecificProperties verifies that when a
+// module that has different output files for a native bridge target requests the native bridge
+// variants are copied into the sdk snapshot that it reports an error.
+func TestSnapshotWithCcHeadersLibrary_DetectsNativeBridgeSpecificProperties(t *testing.T) {
+	android.GroupFixturePreparers(
+		cc.PrepareForTestWithCcDefaultModules,
+		PrepareForTestWithSdkBuildComponents,
+		ccTestFs.AddToFixture(),
+		prepareForTestWithNativeBridgeTarget,
+	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+		`\QArchitecture variant "arm64_native_bridge" of sdk member "mynativeheaders" has properties distinct from other variants; this is not yet supported. The properties are:
+        export_include_dirs: [
+            "arm64_native_bridge/include/myinclude_nativebridge",
+            "arm64_native_bridge/include/myinclude",
+        ],\E`)).
+		RunTestWithBp(t, `
+		sdk {
+			name: "mysdk",
+			native_header_libs: ["mynativeheaders"],
+			traits: {
+				native_bridge_support: ["mynativeheaders"],
+			},
+		}
+
+		cc_library_headers {
+			name: "mynativeheaders",
+			export_include_dirs: ["myinclude"],
+			stl: "none",
+			system_shared_libs: [],
+			native_bridge_supported: true,
+			target: {
+				native_bridge: {
+					export_include_dirs: ["myinclude_nativebridge"],
+				},
+			},
+		}
+	`)
+}
+
 func TestHostSnapshotWithCcHeadersLibrary(t *testing.T) {
 	result := testSdkWithCc(t, `
 		sdk {
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 9efb3a4..43542cb 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -1205,6 +1205,55 @@
 	)
 }
 
+func TestSnapshotWithJavaSdkLibrary_AnnotationsZip(t *testing.T) {
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
+		sdk {
+			name: "mysdk",
+			java_sdk_libs: ["myjavalib"],
+		}
+
+		java_sdk_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			sdk_version: "current",
+			shared_library: false,
+			annotations_enabled: true,
+			public: {
+				enabled: true,
+			},
+		}
+	`)
+
+	CheckSnapshot(t, result, "mysdk", "",
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    shared_library: false,
+    public: {
+        jars: ["sdk_library/public/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+        current_api: "sdk_library/public/myjavalib.txt",
+        removed_api: "sdk_library/public/myjavalib-removed.txt",
+        annotations: "sdk_library/public/myjavalib_annotations.zip",
+        sdk_version: "current",
+    },
+}
+		`),
+		checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_annotations.zip -> sdk_library/public/myjavalib_annotations.zip
+		`),
+		checkMergeZips(".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip"),
+	)
+}
+
 func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) {
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
diff --git a/sdk/member_trait.go b/sdk/member_trait.go
new file mode 100644
index 0000000..4229ca8
--- /dev/null
+++ b/sdk/member_trait.go
@@ -0,0 +1,126 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import (
+	"reflect"
+
+	"android/soong/android"
+	"github.com/google/blueprint/proptools"
+)
+
+// Contains information about the sdk properties that list sdk members by trait, e.g.
+// native_bridge.
+type sdkMemberTraitListProperty struct {
+	// getter for the list of member names
+	getter func(properties interface{}) []string
+
+	// the trait of member referenced in the list
+	memberTrait android.SdkMemberTrait
+}
+
+// Cache of dynamically generated dynamicSdkMemberTraits objects. The key is the pointer
+// to a slice of SdkMemberTrait instances returned by android.RegisteredSdkMemberTraits().
+var dynamicSdkMemberTraitsMap android.OncePer
+
+// A dynamically generated set of member list properties and associated structure type.
+//
+// Instances of this are created by createDynamicSdkMemberTraits.
+type dynamicSdkMemberTraits struct {
+	// The dynamically generated structure type.
+	//
+	// Contains one []string exported field for each SdkMemberTrait returned by android.RegisteredSdkMemberTraits(). The name of
+	// the field is the exported form of the value returned by SdkMemberTrait.SdkPropertyName().
+	propertiesStructType reflect.Type
+
+	// Information about each of the member trait specific list properties.
+	memberTraitListProperties []*sdkMemberTraitListProperty
+}
+
+func (d *dynamicSdkMemberTraits) createMemberTraitListProperties() interface{} {
+	return reflect.New(d.propertiesStructType).Interface()
+}
+
+func getDynamicSdkMemberTraits(key android.OnceKey, registeredTraits []android.SdkMemberTrait) *dynamicSdkMemberTraits {
+	// Get the cached value, creating new instance if necessary.
+	return dynamicSdkMemberTraitsMap.Once(key, func() interface{} {
+		return createDynamicSdkMemberTraits(registeredTraits)
+	}).(*dynamicSdkMemberTraits)
+}
+
+// Create the dynamicSdkMemberTraits from the list of registered member traits.
+//
+// A struct is created which contains one exported field per member trait corresponding to
+// the SdkMemberTrait.SdkPropertyName() value.
+//
+// A list of sdkMemberTraitListProperty instances is created, one per member trait that provides:
+// * a reference to the member trait.
+// * a getter for the corresponding field in the properties struct.
+//
+func createDynamicSdkMemberTraits(sdkMemberTraits []android.SdkMemberTrait) *dynamicSdkMemberTraits {
+
+	var listProperties []*sdkMemberTraitListProperty
+	memberTraitToProperty := map[android.SdkMemberTrait]*sdkMemberTraitListProperty{}
+	var fields []reflect.StructField
+
+	// Iterate over the member traits creating StructField and sdkMemberTraitListProperty objects.
+	nextFieldIndex := 0
+	for _, memberTrait := range sdkMemberTraits {
+
+		p := memberTrait.SdkPropertyName()
+
+		var getter func(properties interface{}) []string
+
+		// Create a dynamic exported field for the member trait's property.
+		fields = append(fields, reflect.StructField{
+			Name: proptools.FieldNameForProperty(p),
+			Type: reflect.TypeOf([]string{}),
+		})
+
+		// Copy the field index for use in the getter func as using the loop variable directly will
+		// cause all funcs to use the last value.
+		fieldIndex := nextFieldIndex
+		nextFieldIndex += 1
+
+		getter = func(properties interface{}) []string {
+			// The properties is expected to be of the following form (where
+			// <Module_traits> is the name of an SdkMemberTrait.SdkPropertyName().
+			//     properties *struct {<Module_traits> []string, ....}
+			//
+			// Although it accesses the field by index the following reflection code is equivalent to:
+			//    *properties.<Module_traits>
+			//
+			list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
+			return list
+		}
+
+		// Create an sdkMemberTraitListProperty for the member trait.
+		memberListProperty := &sdkMemberTraitListProperty{
+			getter:      getter,
+			memberTrait: memberTrait,
+		}
+
+		memberTraitToProperty[memberTrait] = memberListProperty
+		listProperties = append(listProperties, memberListProperty)
+	}
+
+	// Create a dynamic struct from the collated fields.
+	propertiesStructType := reflect.StructOf(fields)
+
+	return &dynamicSdkMemberTraits{
+		memberTraitListProperties: listProperties,
+		propertiesStructType:      propertiesStructType,
+	}
+}
diff --git a/sdk/member_trait_test.go b/sdk/member_trait_test.go
new file mode 100644
index 0000000..a3db189
--- /dev/null
+++ b/sdk/member_trait_test.go
@@ -0,0 +1,287 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import (
+	"fmt"
+	"path/filepath"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/java"
+	"github.com/google/blueprint"
+)
+
+type fakeMemberTrait struct {
+	android.SdkMemberTraitBase
+}
+
+type fakeMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (t *fakeMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	for _, name := range names {
+		ctx.AddVariationDependencies(nil, dependencyTag, name)
+
+		if ctx.RequiresTrait(name, extraTrait) {
+			ctx.AddVariationDependencies(nil, dependencyTag, name+"_extra")
+		}
+		if ctx.RequiresTrait(name, specialTrait) {
+			ctx.AddVariationDependencies(nil, dependencyTag, name+"_special")
+		}
+	}
+}
+
+func (t *fakeMemberType) IsInstance(module android.Module) bool {
+	return true
+}
+
+func (t *fakeMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
+	moduleType := "java_import"
+	if ctx.RequiresTrait(extraTrait) {
+		moduleType = "java_test_import"
+	}
+	return ctx.SnapshotBuilder().AddPrebuiltModule(member, moduleType)
+}
+
+func (t *fakeMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+	return &fakeMemberTypeProperties{}
+}
+
+type fakeMemberTypeProperties struct {
+	android.SdkMemberPropertiesBase
+
+	path android.Path
+}
+
+func (t *fakeMemberTypeProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
+	headerJars := variant.(java.ApexDependency).HeaderJars()
+	if len(headerJars) != 1 {
+		panic(fmt.Errorf("there must be only one header jar from %q", variant.Name()))
+	}
+
+	t.path = headerJars[0]
+}
+
+func (t *fakeMemberTypeProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
+	if t.path != nil {
+		relative := filepath.Join("javalibs", t.path.Base())
+		ctx.SnapshotBuilder().CopyToSnapshot(t.path, relative)
+		propertySet.AddProperty("jars", []string{relative})
+	}
+}
+
+var (
+	extraTrait = &fakeMemberTrait{
+		SdkMemberTraitBase: android.SdkMemberTraitBase{
+			PropertyName: "extra",
+		},
+	}
+
+	specialTrait = &fakeMemberTrait{
+		SdkMemberTraitBase: android.SdkMemberTraitBase{
+			PropertyName: "special",
+		},
+	}
+
+	fakeType = &fakeMemberType{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
+			PropertyName: "fake_members",
+			SupportsSdk:  true,
+			Traits: []android.SdkMemberTrait{
+				extraTrait,
+				specialTrait,
+			},
+		},
+	}
+)
+
+func init() {
+	android.RegisterSdkMemberTrait(extraTrait)
+	android.RegisterSdkMemberTrait(specialTrait)
+	android.RegisterSdkMemberType(fakeType)
+}
+
+func TestBasicTrait_WithoutTrait(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.FixtureWithRootAndroidBp(`
+			sdk {
+				name: "mysdk",
+				fake_members: ["myjavalib"],
+			}
+
+			java_library {
+				name: "myjavalib",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+			}
+		`),
+	).RunTest(t)
+
+	CheckSnapshot(t, result, "mysdk", "",
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["javalibs/myjavalib.jar"],
+}
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["javalibs/myjavalib.jar"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    visibility: ["//visibility:public"],
+    fake_members: ["mysdk_myjavalib@current"],
+}
+`),
+	)
+}
+
+func TestBasicTrait_MultipleTraits(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.FixtureWithRootAndroidBp(`
+			sdk {
+				name: "mysdk",
+				fake_members: ["myjavalib", "anotherjavalib"],
+				traits: {
+					extra: ["myjavalib"],
+					special: ["myjavalib", "anotherjavalib"],
+				},
+			}
+
+			java_library {
+				name: "myjavalib",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+			}
+
+			java_library {
+				name: "myjavalib_extra",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+			}
+
+			java_library {
+				name: "myjavalib_special",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+			}
+
+			java_library {
+				name: "anotherjavalib",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+			}
+
+			java_library {
+				name: "anotherjavalib_special",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+			}
+		`),
+	).RunTest(t)
+
+	CheckSnapshot(t, result, "mysdk", "",
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_test_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["javalibs/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib_extra",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["javalibs/myjavalib_extra.jar"],
+}
+
+java_import {
+    name: "myjavalib_special",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["javalibs/myjavalib_special.jar"],
+}
+
+java_import {
+    name: "anotherjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["javalibs/anotherjavalib.jar"],
+}
+
+java_import {
+    name: "anotherjavalib_special",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["javalibs/anotherjavalib_special.jar"],
+}
+`),
+	)
+}
+
+func TestTraitUnsupportedByMemberType(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.FixtureWithRootAndroidBp(`
+			sdk {
+				name: "mysdk",
+				java_header_libs: ["myjavalib"],
+				traits: {
+					extra: ["myjavalib"],
+				},
+			}
+
+			java_library {
+				name: "myjavalib",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+			}
+		`),
+	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+		`\Qsdk member "myjavalib" has traits [extra] that are unsupported by its member type "java_header_libs"\E`)).
+		RunTest(t)
+}
diff --git a/sdk/member_type.go b/sdk/member_type.go
index 9aab61d..10669fe 100644
--- a/sdk/member_type.go
+++ b/sdk/member_type.go
@@ -64,14 +64,7 @@
 	return reflect.New(d.propertiesStructType).Interface()
 }
 
-func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamicSdkMemberTypes {
-
-	// Get a key that uniquely identifies the registry contents.
-	key := registry.UniqueOnceKey()
-
-	// Get the registered types.
-	registeredTypes := registry.RegisteredTypes()
-
+func getDynamicSdkMemberTypes(key android.OnceKey, registeredTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
 	// Get the cached value, creating new instance if necessary.
 	return dynamicSdkMemberTypesMap.Once(key, func() interface{} {
 		return createDynamicSdkMemberTypes(registeredTypes)
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 6dea752..84c9a96 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -53,6 +53,13 @@
 	// list properties, e.g. java_libs.
 	dynamicMemberTypeListProperties interface{}
 
+	// The dynamically generated information about the registered SdkMemberTrait
+	dynamicSdkMemberTraits *dynamicSdkMemberTraits
+
+	// The dynamically created instance of the properties struct containing the sdk member trait
+	// list properties.
+	dynamicMemberTraitListProperties interface{}
+
 	// Information about the OsType specific member variants depended upon by this variant.
 	//
 	// Set by OsType specific variants in the collectMembers() method and used by the
@@ -104,17 +111,25 @@
 	s := &sdk{}
 	s.properties.Module_exports = moduleExports
 	// Get the dynamic sdk member type data for the currently registered sdk member types.
-	var typeRegistry *android.SdkMemberTypesRegistry
-	if moduleExports {
-		typeRegistry = android.ModuleExportsMemberTypes
-	} else {
-		typeRegistry = android.SdkMemberTypes
-	}
-	s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(typeRegistry)
+	sdkMemberTypeKey, sdkMemberTypes := android.RegisteredSdkMemberTypes(moduleExports)
+	s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(sdkMemberTypeKey, sdkMemberTypes)
 	// Create an instance of the dynamically created struct that contains all the
 	// properties for the member type specific list properties.
 	s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberTypeListProperties()
-	s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties)
+
+	sdkMemberTraitsKey, sdkMemberTraits := android.RegisteredSdkMemberTraits()
+	s.dynamicSdkMemberTraits = getDynamicSdkMemberTraits(sdkMemberTraitsKey, sdkMemberTraits)
+	// Create an instance of the dynamically created struct that contains all the properties for the
+	// member trait specific list properties.
+	s.dynamicMemberTraitListProperties = s.dynamicSdkMemberTraits.createMemberTraitListProperties()
+
+	// Create a wrapper around the dynamic trait specific properties so that they have to be
+	// specified within a traits:{} section in the .bp file.
+	traitsWrapper := struct {
+		Traits interface{}
+	}{s.dynamicMemberTraitListProperties}
+
+	s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties, &traitsWrapper)
 
 	// Make sure that the prebuilt visibility property is verified for errors.
 	android.AddVisibilityProperty(s, "prebuilt_visibility", &s.properties.Prebuilt_visibility)
@@ -145,6 +160,11 @@
 	return s.dynamicSdkMemberTypes.memberTypeToProperty[memberType]
 }
 
+// memberTraitListProperties returns the list of *sdkMemberTraitListProperty instances for this sdk.
+func (s *sdk) memberTraitListProperties() []*sdkMemberTraitListProperty {
+	return s.dynamicSdkMemberTraits.memberTraitListProperties
+}
+
 func (s *sdk) snapshot() bool {
 	return s.properties.Snapshot
 }
@@ -198,15 +218,53 @@
 	}}
 }
 
+// gatherTraits gathers the traits from the dynamically generated trait specific properties.
+//
+// Returns a map from member name to the set of required traits.
+func (s *sdk) gatherTraits() map[string]android.SdkMemberTraitSet {
+	traitListByMember := map[string][]android.SdkMemberTrait{}
+	for _, memberListProperty := range s.memberTraitListProperties() {
+		names := memberListProperty.getter(s.dynamicMemberTraitListProperties)
+		for _, name := range names {
+			traitListByMember[name] = append(traitListByMember[name], memberListProperty.memberTrait)
+		}
+	}
+
+	traitSetByMember := map[string]android.SdkMemberTraitSet{}
+	for name, list := range traitListByMember {
+		traitSetByMember[name] = android.NewSdkMemberTraitSet(list)
+	}
+
+	return traitSetByMember
+}
+
 // newDependencyContext creates a new SdkDependencyContext for this sdk.
 func (s *sdk) newDependencyContext(mctx android.BottomUpMutatorContext) android.SdkDependencyContext {
+	traits := s.gatherTraits()
+
 	return &dependencyContext{
 		BottomUpMutatorContext: mctx,
+		requiredTraits:         traits,
 	}
 }
 
 type dependencyContext struct {
 	android.BottomUpMutatorContext
+
+	// Map from member name to the set of traits that the sdk requires the member provides.
+	requiredTraits map[string]android.SdkMemberTraitSet
+}
+
+func (d *dependencyContext) RequiredTraits(name string) android.SdkMemberTraitSet {
+	if s, ok := d.requiredTraits[name]; ok {
+		return s
+	} else {
+		return android.EmptySdkMemberTraitSet()
+	}
+}
+
+func (d *dependencyContext) RequiresTrait(name string, trait android.SdkMemberTrait) bool {
+	return d.RequiredTraits(name).Contains(trait)
 }
 
 var _ android.SdkDependencyContext = (*dependencyContext)(nil)
@@ -287,8 +345,21 @@
 				}
 				names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
 				if len(names) > 0 {
+					memberType := memberListProperty.memberType
+
+					// Verify that the member type supports the specified traits.
+					supportedTraits := memberType.SupportedTraits()
+					for _, name := range names {
+						requiredTraits := ctx.RequiredTraits(name)
+						unsupportedTraits := requiredTraits.Subtract(supportedTraits)
+						if !unsupportedTraits.Empty() {
+							ctx.ModuleErrorf("sdk member %q has traits %s that are unsupported by its member type %q", name, unsupportedTraits, memberType.SdkPropertyName())
+						}
+					}
+
+					// Add dependencies using the appropriate tag.
 					tag := memberListProperty.dependencyTag
-					memberListProperty.memberType.AddDependencies(ctx, tag, names)
+					memberType.AddDependencies(ctx, tag, names)
 				}
 			}
 		}
diff --git a/sdk/update.go b/sdk/update.go
index 89a5c92..02e61fb 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -393,10 +393,18 @@
 	members := s.groupMemberVariantsByMemberThenType(ctx, memberVariantDeps)
 
 	// Create the prebuilt modules for each of the member modules.
+	traits := s.gatherTraits()
 	for _, member := range members {
 		memberType := member.memberType
 
-		memberCtx := &memberContext{ctx, builder, memberType, member.name}
+		name := member.name
+		requiredTraits := traits[name]
+		if requiredTraits == nil {
+			requiredTraits = android.EmptySdkMemberTraitSet()
+		}
+
+		// Create the snapshot for the member.
+		memberCtx := &memberContext{ctx, builder, memberType, name, requiredTraits}
 
 		prebuiltModule := memberType.AddPrebuiltModule(memberCtx, member)
 		s.createMemberSnapshot(memberCtx, member, prebuiltModule.(*bpModule))
@@ -1385,19 +1393,19 @@
 	osInfo.Properties = osSpecificVariantPropertiesFactory()
 
 	// Group the variants by arch type.
-	var variantsByArchName = make(map[string][]android.Module)
-	var archTypes []android.ArchType
+	var variantsByArchId = make(map[archId][]android.Module)
+	var archIds []archId
 	for _, variant := range osTypeVariants {
-		archType := variant.Target().Arch.ArchType
-		archTypeName := archType.Name
-		if _, ok := variantsByArchName[archTypeName]; !ok {
-			archTypes = append(archTypes, archType)
+		target := variant.Target()
+		id := archIdFromTarget(target)
+		if _, ok := variantsByArchId[id]; !ok {
+			archIds = append(archIds, id)
 		}
 
-		variantsByArchName[archTypeName] = append(variantsByArchName[archTypeName], variant)
+		variantsByArchId[id] = append(variantsByArchId[id], variant)
 	}
 
-	if commonVariants, ok := variantsByArchName["common"]; ok {
+	if commonVariants, ok := variantsByArchId[commonArchId]; ok {
 		if len(osTypeVariants) != 1 {
 			panic(fmt.Errorf("Expected to only have 1 variant when arch type is common but found %d", len(osTypeVariants)))
 		}
@@ -1407,11 +1415,9 @@
 		osInfo.Properties.PopulateFromVariant(ctx, commonVariants[0])
 	} else {
 		// Create an arch specific info for each supported architecture type.
-		for _, archType := range archTypes {
-			archTypeName := archType.Name
-
-			archVariants := variantsByArchName[archTypeName]
-			archInfo := newArchSpecificInfo(ctx, archType, osType, osSpecificVariantPropertiesFactory, archVariants)
+		for _, id := range archIds {
+			archVariants := variantsByArchId[id]
+			archInfo := newArchSpecificInfo(ctx, id, osType, osSpecificVariantPropertiesFactory, archVariants)
 
 			osInfo.archInfos = append(osInfo.archInfos, archInfo)
 		}
@@ -1430,7 +1436,7 @@
 
 	multilib := multilibNone
 	for _, archInfo := range osInfo.archInfos {
-		multilib = multilib.addArchType(archInfo.archType)
+		multilib = multilib.addArchType(archInfo.archId.archType)
 
 		// Optimize the arch properties first.
 		archInfo.optimizeProperties(ctx, commonValueExtractor)
@@ -1523,11 +1529,55 @@
 	return fmt.Sprintf("OsType{%s}", osInfo.osType)
 }
 
+// archId encapsulates the information needed to identify a combination of arch type and native
+// bridge support.
+//
+// Conceptually, native bridge support is a facet of an android.Target, not an android.Arch as it is
+// essentially using one android.Arch to implement another. However, in terms of the handling of
+// the variants native bridge is treated as part of the arch variation. See the ArchVariation method
+// on android.Target.
+//
+// So, it makes sense when optimizing the variants to combine native bridge with the arch type.
+type archId struct {
+	// The arch type of the variant's target.
+	archType android.ArchType
+
+	// True if the variants is for the native bridge, false otherwise.
+	nativeBridge bool
+}
+
+// propertyName returns the name of the property corresponding to use for this arch id.
+func (i *archId) propertyName() string {
+	name := i.archType.Name
+	if i.nativeBridge {
+		// Note: This does not result in a valid property because there is no architecture specific
+		// native bridge property, only a generic "native_bridge" property. However, this will be used
+		// in error messages if there is an attempt to use this in a generated bp file.
+		name += "_native_bridge"
+	}
+	return name
+}
+
+func (i *archId) String() string {
+	return fmt.Sprintf("ArchType{%s}, NativeBridge{%t}", i.archType, i.nativeBridge)
+}
+
+// archIdFromTarget returns an archId initialized from information in the supplied target.
+func archIdFromTarget(target android.Target) archId {
+	return archId{
+		archType:     target.Arch.ArchType,
+		nativeBridge: target.NativeBridge == android.NativeBridgeEnabled,
+	}
+}
+
+// commonArchId is the archId for the common architecture.
+var commonArchId = archId{archType: android.Common}
+
 type archTypeSpecificInfo struct {
 	baseInfo
 
-	archType android.ArchType
-	osType   android.OsType
+	archId archId
+	osType android.OsType
 
 	linkInfos []*linkTypeSpecificInfo
 }
@@ -1536,10 +1586,10 @@
 
 // Create a new archTypeSpecificInfo for the specified arch type and its properties
 // structures populated with information from the variants.
-func newArchSpecificInfo(ctx android.SdkMemberContext, archType android.ArchType, osType android.OsType, variantPropertiesFactory variantPropertiesFactoryFunc, archVariants []android.Module) *archTypeSpecificInfo {
+func newArchSpecificInfo(ctx android.SdkMemberContext, archId archId, osType android.OsType, variantPropertiesFactory variantPropertiesFactoryFunc, archVariants []android.Module) *archTypeSpecificInfo {
 
 	// Create an arch specific info into which the variant properties can be copied.
-	archInfo := &archTypeSpecificInfo{archType: archType, osType: osType}
+	archInfo := &archTypeSpecificInfo{archId: archId, osType: osType}
 
 	// Create the properties into which the arch type specific properties will be
 	// added.
@@ -1597,8 +1647,9 @@
 
 // Add the properties for an arch type to a property set.
 func (archInfo *archTypeSpecificInfo) addToPropertySet(ctx *memberContext, archPropertySet android.BpPropertySet, archOsPrefix string) {
-	archTypeName := archInfo.archType.Name
-	archTypePropertySet := archPropertySet.AddPropertySet(archOsPrefix + archTypeName)
+	archPropertySuffix := archInfo.archId.propertyName()
+	propertySetName := archOsPrefix + archPropertySuffix
+	archTypePropertySet := archPropertySet.AddPropertySet(propertySetName)
 	// Enable the <os>_<arch> variant explicitly when we've disabled it by default on host.
 	if ctx.memberType.IsHostOsDependent() && archInfo.osType.Class == android.Host {
 		archTypePropertySet.AddProperty("enabled", true)
@@ -1608,10 +1659,36 @@
 	for _, linkInfo := range archInfo.linkInfos {
 		linkInfo.addToPropertySet(ctx, archTypePropertySet)
 	}
+
+	// If this is for a native bridge architecture then make sure that the property set does not
+	// contain any properties as providing native bridge specific properties is not currently
+	// supported.
+	if archInfo.archId.nativeBridge {
+		propertySetContents := getPropertySetContents(archTypePropertySet)
+		if propertySetContents != "" {
+			ctx.SdkModuleContext().ModuleErrorf("Architecture variant %q of sdk member %q has properties distinct from other variants; this is not yet supported. The properties are:\n%s",
+				propertySetName, ctx.name, propertySetContents)
+		}
+	}
+}
+
+// getPropertySetContents returns the string representation of the contents of a property set, after
+// recursively pruning any empty nested property sets.
+func getPropertySetContents(propertySet android.BpPropertySet) string {
+	set := propertySet.(*bpPropertySet)
+	set.transformContents(pruneEmptySetTransformer{})
+	if len(set.properties) != 0 {
+		contents := &generatedContents{}
+		contents.Indent()
+		outputPropertySet(contents, set)
+		setAsString := contents.content.String()
+		return setAsString
+	}
+	return ""
 }
 
 func (archInfo *archTypeSpecificInfo) String() string {
-	return fmt.Sprintf("ArchType{%s}", archInfo.archType)
+	return archInfo.archId.String()
 }
 
 type linkTypeSpecificInfo struct {
@@ -1651,6 +1728,9 @@
 	builder          *snapshotBuilder
 	memberType       android.SdkMemberType
 	name             string
+
+	// The set of traits required of this member.
+	requiredTraits android.SdkMemberTraitSet
 }
 
 func (m *memberContext) SdkModuleContext() android.ModuleContext {
@@ -1669,6 +1749,10 @@
 	return m.name
 }
 
+func (m *memberContext) RequiresTrait(trait android.SdkMemberTrait) bool {
+	return m.requiredTraits.Contains(trait)
+}
+
 func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModule *bpModule) {
 
 	memberType := member.memberType
diff --git a/ui/terminal/simple_status.go b/ui/terminal/simple_status.go
index 4e8c568..936b275 100644
--- a/ui/terminal/simple_status.go
+++ b/ui/terminal/simple_status.go
@@ -24,15 +24,17 @@
 type simpleStatusOutput struct {
 	writer    io.Writer
 	formatter formatter
+	keepANSI  bool
 }
 
 // NewSimpleStatusOutput returns a StatusOutput that represents the
 // current build status similarly to Ninja's built-in terminal
 // output.
-func NewSimpleStatusOutput(w io.Writer, formatter formatter) status.StatusOutput {
+func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool) status.StatusOutput {
 	return &simpleStatusOutput{
 		writer:    w,
 		formatter: formatter,
+		keepANSI:  keepANSI,
 	}
 }
 
@@ -54,7 +56,9 @@
 	progress := s.formatter.progress(counts) + str
 
 	output := s.formatter.result(result)
-	output = string(stripAnsiEscapes([]byte(output)))
+	if !s.keepANSI {
+		output = string(stripAnsiEscapes([]byte(output)))
+	}
 
 	if output != "" {
 		fmt.Fprint(s.writer, progress, "\n", output)
diff --git a/ui/terminal/smart_status.go b/ui/terminal/smart_status.go
index 6bdf140..06a4064 100644
--- a/ui/terminal/smart_status.go
+++ b/ui/terminal/smart_status.go
@@ -77,7 +77,12 @@
 		s.requestedTableHeight = h
 	}
 
-	s.updateTermSize()
+	if w, h, ok := termSize(s.writer); ok {
+		s.termWidth, s.termHeight = w, h
+		s.computeTableHeight()
+	} else {
+		s.tableMode = false
+	}
 
 	if s.tableMode {
 		// Add empty lines at the bottom of the screen to scroll back the existing history
@@ -296,40 +301,44 @@
 	close(s.sigwinch)
 }
 
+// computeTableHeight recomputes s.tableHeight based on s.termHeight and s.requestedTableHeight.
+func (s *smartStatusOutput) computeTableHeight() {
+	tableHeight := s.requestedTableHeight
+	if tableHeight == 0 {
+		tableHeight = s.termHeight / 4
+		if tableHeight < 1 {
+			tableHeight = 1
+		} else if tableHeight > 10 {
+			tableHeight = 10
+		}
+	}
+	if tableHeight > s.termHeight-1 {
+		tableHeight = s.termHeight - 1
+	}
+	s.tableHeight = tableHeight
+}
+
+// updateTermSize recomputes the table height after a SIGWINCH and pans any existing text if
+// necessary.
 func (s *smartStatusOutput) updateTermSize() {
 	if w, h, ok := termSize(s.writer); ok {
-		firstUpdate := s.termHeight == 0 && s.termWidth == 0
 		oldScrollingHeight := s.termHeight - s.tableHeight
 
 		s.termWidth, s.termHeight = w, h
 
 		if s.tableMode {
-			tableHeight := s.requestedTableHeight
-			if tableHeight == 0 {
-				tableHeight = s.termHeight / 4
-				if tableHeight < 1 {
-					tableHeight = 1
-				} else if tableHeight > 10 {
-					tableHeight = 10
-				}
-			}
-			if tableHeight > s.termHeight-1 {
-				tableHeight = s.termHeight - 1
-			}
-			s.tableHeight = tableHeight
+			s.computeTableHeight()
 
 			scrollingHeight := s.termHeight - s.tableHeight
 
-			if !firstUpdate {
-				// If the scrolling region has changed, attempt to pan the existing text so that it is
-				// not overwritten by the table.
-				if scrollingHeight < oldScrollingHeight {
-					pan := oldScrollingHeight - scrollingHeight
-					if pan > s.tableHeight {
-						pan = s.tableHeight
-					}
-					fmt.Fprint(s.writer, ansi.panDown(pan))
+			// If the scrolling region has changed, attempt to pan the existing text so that it is
+			// not overwritten by the table.
+			if scrollingHeight < oldScrollingHeight {
+				pan := oldScrollingHeight - scrollingHeight
+				if pan > s.tableHeight {
+					pan = s.tableHeight
 				}
+				fmt.Fprint(s.writer, ansi.panDown(pan))
 			}
 		}
 	}
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index d8e7392..2ad174f 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -26,12 +26,12 @@
 //
 // statusFormat takes nearly all the same options as NINJA_STATUS.
 // %c is currently unsupported.
-func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild bool) status.StatusOutput {
+func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput {
 	formatter := newFormatter(statusFormat, quietBuild)
 
 	if !forceSimpleOutput && isSmartTerminal(w) {
 		return NewSmartStatusOutput(w, formatter)
 	} else {
-		return NewSimpleStatusOutput(w, formatter)
+		return NewSimpleStatusOutput(w, formatter, forceKeepANSI)
 	}
 }
diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go
index aa69dff..810e31d 100644
--- a/ui/terminal/status_test.go
+++ b/ui/terminal/status_test.go
@@ -94,7 +94,7 @@
 
 			t.Run("smart", func(t *testing.T) {
 				smart := &fakeSmartTerminal{termWidth: 40}
-				stat := NewStatusOutput(smart, "", false, false)
+				stat := NewStatusOutput(smart, "", false, false, false)
 				tt.calls(stat)
 				stat.Flush()
 
@@ -105,7 +105,7 @@
 
 			t.Run("simple", func(t *testing.T) {
 				simple := &bytes.Buffer{}
-				stat := NewStatusOutput(simple, "", false, false)
+				stat := NewStatusOutput(simple, "", false, false, false)
 				tt.calls(stat)
 				stat.Flush()
 
@@ -116,7 +116,7 @@
 
 			t.Run("force simple", func(t *testing.T) {
 				smart := &fakeSmartTerminal{termWidth: 40}
-				stat := NewStatusOutput(smart, "", true, false)
+				stat := NewStatusOutput(smart, "", true, false, false)
 				tt.calls(stat)
 				stat.Flush()
 
@@ -269,7 +269,7 @@
 	os.Setenv(tableHeightEnVar, "")
 
 	smart := &fakeSmartTerminal{termWidth: 40}
-	stat := NewStatusOutput(smart, "", false, false)
+	stat := NewStatusOutput(smart, "", false, false, false)
 	smartStat := stat.(*smartStatusOutput)
 	smartStat.sigwinchHandled = make(chan bool)