Merge "Add android.hardware.light-ndk_platform to vndkMustUseVendorVariantList"
diff --git a/android/defaults.go b/android/defaults.go
index 7597446..fd707a4 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"reflect"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -30,16 +32,18 @@
 }
 
 type DefaultableModuleBase struct {
-	defaultsProperties    defaultsProperties
-	defaultableProperties []interface{}
+	defaultsProperties            defaultsProperties
+	defaultableProperties         []interface{}
+	defaultableVariableProperties interface{}
 }
 
 func (d *DefaultableModuleBase) defaults() *defaultsProperties {
 	return &d.defaultsProperties
 }
 
-func (d *DefaultableModuleBase) setProperties(props []interface{}) {
+func (d *DefaultableModuleBase) setProperties(props []interface{}, variableProperties interface{}) {
 	d.defaultableProperties = props
+	d.defaultableVariableProperties = variableProperties
 }
 
 // Interface that must be supported by any module to which defaults can be applied.
@@ -48,7 +52,7 @@
 	defaults() *defaultsProperties
 
 	// Set the property structures into which defaults will be added.
-	setProperties([]interface{})
+	setProperties(props []interface{}, variableProperties interface{})
 
 	// Apply defaults from the supplied Defaults to the property structures supplied to
 	// setProperties(...).
@@ -63,7 +67,10 @@
 var _ Defaultable = (*DefaultableModuleBase)(nil)
 
 func InitDefaultableModule(module DefaultableModule) {
-	module.setProperties(module.(Module).GetProperties())
+	if module.(Module).base().module == nil {
+		panic("InitAndroidModule must be called before InitDefaultableModule")
+	}
+	module.setProperties(module.(Module).GetProperties(), module.(Module).base().variableProperties)
 
 	module.AddProperties(module.defaults())
 }
@@ -114,6 +121,8 @@
 	// Get the structures containing the properties for which defaults can be provided.
 	properties() []interface{}
 
+	productVariableProperties() interface{}
+
 	// Return the defaults common properties.
 	common() *commonProperties
 
@@ -134,6 +143,10 @@
 	return d.defaultableProperties
 }
 
+func (d *DefaultsModuleBase) productVariableProperties() interface{} {
+	return d.defaultableVariableProperties
+}
+
 func (d *DefaultsModuleBase) common() *commonProperties {
 	return &d.commonProperties
 }
@@ -151,9 +164,10 @@
 	module.AddProperties(
 		&hostAndDeviceProperties{},
 		commonProperties,
-		&variableProperties{},
 		&ApexProperties{})
 
+	initAndroidModuleBase(module)
+	initProductVariableModule(module)
 	InitArchModule(module)
 	InitDefaultableModule(module)
 
@@ -185,16 +199,57 @@
 
 	for _, defaults := range defaultsList {
 		for _, prop := range defaultable.defaultableProperties {
-			for _, def := range defaults.properties() {
-				if proptools.TypeEqual(prop, def) {
-					err := proptools.PrependProperties(prop, def, nil)
-					if err != nil {
-						if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-							ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
-						} else {
-							panic(err)
-						}
-					}
+			if prop == defaultable.defaultableVariableProperties {
+				defaultable.applyDefaultVariableProperties(ctx, defaults, prop)
+			} else {
+				defaultable.applyDefaultProperties(ctx, defaults, prop)
+			}
+		}
+	}
+}
+
+// Product variable properties need special handling, the type of the filtered product variable
+// property struct may not be identical between the defaults module and the defaultable module.
+// Use PrependMatchingProperties to apply whichever properties match.
+func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx TopDownMutatorContext,
+	defaults Defaults, defaultableProp interface{}) {
+	if defaultableProp == nil {
+		return
+	}
+
+	defaultsProp := defaults.productVariableProperties()
+	if defaultsProp == nil {
+		return
+	}
+
+	dst := []interface{}{
+		defaultableProp,
+		// Put an empty copy of the src properties into dst so that properties in src that are not in dst
+		// don't cause a "failed to find property to extend" error.
+		proptools.CloneEmptyProperties(reflect.ValueOf(defaultsProp)).Interface(),
+	}
+
+	err := proptools.PrependMatchingProperties(dst, defaultsProp, nil)
+	if err != nil {
+		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+		} else {
+			panic(err)
+		}
+	}
+}
+
+func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx TopDownMutatorContext,
+	defaults Defaults, defaultableProp interface{}) {
+
+	for _, def := range defaults.properties() {
+		if proptools.TypeEqual(defaultableProp, def) {
+			err := proptools.PrependProperties(defaultableProp, def, nil)
+			if err != nil {
+				if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+					ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+				} else {
+					panic(err)
 				}
 			}
 		}
diff --git a/android/defaults_test.go b/android/defaults_test.go
index ba607ef..d096b2f 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"reflect"
 	"testing"
 
 	"github.com/google/blueprint/proptools"
@@ -40,8 +41,8 @@
 func defaultsTestModuleFactory() Module {
 	module := &defaultsTestModule{}
 	module.AddProperties(&module.properties)
-	InitDefaultableModule(module)
 	InitAndroidModule(module)
+	InitDefaultableModule(module)
 	return module
 }
 
@@ -57,6 +58,49 @@
 	return defaults
 }
 
+func TestDefaults(t *testing.T) {
+	bp := `
+		defaults {
+			name: "transitive",
+			foo: ["transitive"],
+		}
+
+		defaults {
+			name: "defaults",
+			defaults: ["transitive"],
+			foo: ["defaults"],
+		}
+
+		test {
+			name: "foo",
+			defaults: ["defaults"],
+			foo: ["module"],
+		}
+	`
+
+	config := TestConfig(buildDir, nil, bp, nil)
+
+	ctx := NewTestContext()
+
+	ctx.RegisterModuleType("test", defaultsTestModuleFactory)
+	ctx.RegisterModuleType("defaults", defaultsTestDefaultsFactory)
+
+	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	FailIfErrored(t, errs)
+
+	foo := ctx.ModuleForTests("foo", "").Module().(*defaultsTestModule)
+
+	if g, w := foo.properties.Foo, []string{"transitive", "defaults", "module"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("expected foo %q, got %q", w, g)
+	}
+}
+
 func TestDefaultsAllowMissingDependencies(t *testing.T) {
 	bp := `
 		defaults {
diff --git a/android/module.go b/android/module.go
index 96c2e1e..0a8737b 100644
--- a/android/module.go
+++ b/android/module.go
@@ -537,16 +537,7 @@
 		&base.nameProperties,
 		&base.commonProperties)
 
-	// Allow tests to override the default product variables
-	if base.variableProperties == nil {
-		base.variableProperties = zeroProductVariables
-	}
-
-	// Filter the product variables properties to the ones that exist on this module
-	base.variableProperties = createVariableProperties(m.GetProperties(), base.variableProperties)
-	if base.variableProperties != nil {
-		m.AddProperties(base.variableProperties)
-	}
+	initProductVariableModule(m)
 
 	base.generalProperties = m.GetProperties()
 	base.customizableProperties = m.GetProperties()
diff --git a/android/prebuilt.go b/android/prebuilt.go
index c780cb2..2d16f65 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -62,6 +62,10 @@
 	return "prebuilt_" + name
 }
 
+func (p *Prebuilt) ForcePrefer() {
+	p.properties.Prefer = proptools.BoolPtr(true)
+}
+
 // The below source-related functions and the srcs, src fields are based on an assumption that
 // prebuilt modules have a static source property at the moment. Currently there is only one
 // exception, android_app_import, which chooses a source file depending on the product's DPI
diff --git a/android/sdk.go b/android/sdk.go
index 27756ce..d13ad7d 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -180,6 +180,15 @@
 	// will only be used if the equivalently named non-prebuilt module is not
 	// present.
 	AddPrebuiltModule(member SdkMember, moduleType string) BpModule
+
+	// The property tag to use when adding a property to a BpModule that contains
+	// references to other sdk members. Using this will ensure that the reference
+	// is correctly output for both versioned and unversioned prebuilts in the
+	// snapshot.
+	//
+	// e.g.
+	// bpPropertySet.AddPropertyWithTag("libs", []string{"member1", "member2"}, builder.SdkMemberReferencePropertyTag())
+	SdkMemberReferencePropertyTag() BpPropertyTag
 }
 
 type BpPropertyTag interface{}
@@ -264,6 +273,13 @@
 	// True if the member type supports the sdk/sdk_snapshot, false otherwise.
 	UsableWithSdkAndSdkSnapshot() bool
 
+	// Return true if modules of this type can have dependencies which should be
+	// treated as if they are sdk members.
+	//
+	// Any dependency that is to be treated as a member of the sdk needs to implement
+	// SdkAware and be added with an SdkMemberTypeDependencyTag tag.
+	HasTransitiveSdkMembers() bool
+
 	// Add dependencies from the SDK module to all the variants the member
 	// contributes to the SDK. The exact set of variants required is determined
 	// by the SDK and its properties. The dependencies must be added with the
@@ -291,8 +307,9 @@
 
 // Base type for SdkMemberType implementations.
 type SdkMemberTypeBase struct {
-	PropertyName string
-	SupportsSdk  bool
+	PropertyName         string
+	SupportsSdk          bool
+	TransitiveSdkMembers bool
 }
 
 func (b *SdkMemberTypeBase) SdkPropertyName() string {
@@ -303,6 +320,10 @@
 	return b.SupportsSdk
 }
 
+func (b *SdkMemberTypeBase) HasTransitiveSdkMembers() bool {
+	return b.TransitiveSdkMembers
+}
+
 // Encapsulates the information about registered SdkMemberTypes.
 type SdkMemberTypesRegistry struct {
 	// The list of types sorted by property name.
diff --git a/android/variable.go b/android/variable.go
index 9625a87..58e5940 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -25,7 +25,7 @@
 
 func init() {
 	PreDepsMutators(func(ctx RegisterMutatorsContext) {
-		ctx.BottomUp("variable", variableMutator).Parallel()
+		ctx.BottomUp("variable", VariableMutator).Parallel()
 	})
 }
 
@@ -127,13 +127,14 @@
 		} `android:"arch_variant"`
 
 		Native_coverage struct {
+			Src          *string  `android:"arch_variant"`
 			Srcs         []string `android:"arch_variant"`
 			Exclude_srcs []string `android:"arch_variant"`
 		} `android:"arch_variant"`
 	} `android:"arch_variant"`
 }
 
-var zeroProductVariables interface{} = variableProperties{}
+var defaultProductVariables interface{} = variableProperties{}
 
 type productVariables struct {
 	// Suffix to add to generated Makefiles
@@ -384,7 +385,7 @@
 	}
 }
 
-func variableMutator(mctx BottomUpMutatorContext) {
+func VariableMutator(mctx BottomUpMutatorContext) {
 	var module Module
 	var ok bool
 	if module, ok = mctx.Module().(Module); !ok {
@@ -399,11 +400,9 @@
 	}
 
 	variableValues := reflect.ValueOf(a.variableProperties).Elem().FieldByName("Product_variables")
-	zeroValues := reflect.ValueOf(zeroProductVariables).FieldByName("Product_variables")
 
 	for i := 0; i < variableValues.NumField(); i++ {
 		variableValue := variableValues.Field(i)
-		zeroValue := zeroValues.Field(i)
 		name := variableValues.Type().Field(i).Name
 		property := "product_variables." + proptools.PropertyNameForField(name)
 
@@ -421,10 +420,9 @@
 		}
 
 		// Check if any properties were set for the module
-		if reflect.DeepEqual(variableValue.Interface(), zeroValue.Interface()) {
+		if variableValue.IsZero() {
 			continue
 		}
-
 		a.setVariableProperties(mctx, property, variableValue, val.Interface())
 	}
 }
@@ -542,6 +540,20 @@
 	return ret.Interface()
 }
 
+func initProductVariableModule(m Module) {
+	base := m.base()
+
+	// Allow tests to override the default product variables
+	if base.variableProperties == nil {
+		base.variableProperties = defaultProductVariables
+	}
+	// Filter the product variables properties to the ones that exist on this module
+	base.variableProperties = createVariableProperties(m.GetProperties(), base.variableProperties)
+	if base.variableProperties != nil {
+		m.AddProperties(base.variableProperties)
+	}
+}
+
 // createVariableProperties takes the list of property structs for a module and returns a property struct that
 // contains the product variable properties that exist in the property structs, or nil if there are none.  It
 // caches the result.
diff --git a/android/variable_test.go b/android/variable_test.go
index cde2b1a..9cafedd 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -171,7 +171,7 @@
 		Foo []string
 	}{}))
 	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-		ctx.BottomUp("variable", variableMutator).Parallel()
+		ctx.BottomUp("variable", VariableMutator).Parallel()
 	})
 
 	// Test that a module can use one product variable even if it doesn't have all the properties
@@ -209,6 +209,115 @@
 	FailIfErrored(t, errs)
 }
 
+var testProductVariableDefaultsProperties = struct {
+	Product_variables struct {
+		Eng struct {
+			Foo []string
+			Bar []string
+		}
+	}
+}{}
+
+type productVariablesDefaultsTestProperties struct {
+	Foo []string
+}
+
+type productVariablesDefaultsTestProperties2 struct {
+	Foo []string
+	Bar []string
+}
+
+type productVariablesDefaultsTestModule struct {
+	ModuleBase
+	DefaultableModuleBase
+	properties productVariablesDefaultsTestProperties
+}
+
+func (d *productVariablesDefaultsTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	ctx.Build(pctx, BuildParams{
+		Rule:   Touch,
+		Output: PathForModuleOut(ctx, "out"),
+	})
+}
+
+func productVariablesDefaultsTestModuleFactory() Module {
+	module := &productVariablesDefaultsTestModule{}
+	module.AddProperties(&module.properties)
+	module.variableProperties = testProductVariableDefaultsProperties
+	InitAndroidModule(module)
+	InitDefaultableModule(module)
+	return module
+}
+
+type productVariablesDefaultsTestDefaults struct {
+	ModuleBase
+	DefaultsModuleBase
+}
+
+func productVariablesDefaultsTestDefaultsFactory() Module {
+	defaults := &productVariablesDefaultsTestDefaults{}
+	defaults.AddProperties(&productVariablesDefaultsTestProperties{})
+	defaults.AddProperties(&productVariablesDefaultsTestProperties2{})
+	defaults.variableProperties = testProductVariableDefaultsProperties
+	InitDefaultsModule(defaults)
+	return defaults
+}
+
+// Test a defaults module that supports more product variable properties than the target module.
+func TestProductVariablesDefaults(t *testing.T) {
+	bp := `
+		defaults {
+			name: "defaults",
+			product_variables: {
+				eng: {
+					foo: ["product_variable_defaults"],
+					bar: ["product_variable_defaults"],
+				},
+			},
+			foo: ["defaults"],
+			bar: ["defaults"],
+		}
+
+		test {
+			name: "foo",
+			defaults: ["defaults"],
+			foo: ["module"],
+			product_variables: {
+				eng: {
+					foo: ["product_variable_module"],
+				},
+			},
+		}
+	`
+
+	config := TestConfig(buildDir, nil, bp, nil)
+	config.TestProductVariables.Eng = boolPtr(true)
+
+	ctx := NewTestContext()
+
+	ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory)
+	ctx.RegisterModuleType("defaults", productVariablesDefaultsTestDefaultsFactory)
+
+	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("variable", VariableMutator).Parallel()
+	})
+
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	FailIfErrored(t, errs)
+
+	foo := ctx.ModuleForTests("foo", "").Module().(*productVariablesDefaultsTestModule)
+
+	want := []string{"defaults", "module", "product_variable_defaults", "product_variable_module"}
+	if g, w := foo.properties.Foo, want; !reflect.DeepEqual(g, w) {
+		t.Errorf("expected foo %q, got %q", w, g)
+	}
+}
+
 func BenchmarkSliceToTypeArray(b *testing.B) {
 	for _, n := range []int{1, 2, 4, 8, 100} {
 		var propStructs []interface{}
diff --git a/apex/apex.go b/apex/apex.go
index f42bed0..eafc186 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2054,11 +2054,12 @@
 			}
 			switch depTag {
 			case sharedLibTag:
-				if cc, ok := child.(*cc.Module); ok {
-					if cc.HasStubsVariants() {
-						provideNativeLibs = append(provideNativeLibs, cc.OutputFile().Path().Base())
+				if c, ok := child.(*cc.Module); ok {
+					// bootstrap bionic libs are treated as provided by system
+					if c.HasStubsVariants() && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
+						provideNativeLibs = append(provideNativeLibs, c.OutputFile().Path().Base())
 					}
-					filesInfo = append(filesInfo, apexFileForNativeLibrary(ctx, cc, handleSpecialLibs))
+					filesInfo = append(filesInfo, apexFileForNativeLibrary(ctx, c, handleSpecialLibs))
 					return true // track transitive dependencies
 				} else {
 					ctx.PropertyErrorf("native_shared_libs", "%q is not a cc_library or cc_library_shared module", depName)
@@ -2093,12 +2094,12 @@
 					}
 					filesInfo = append(filesInfo, af)
 
-					pf, _ := sdkLib.OutputFiles(".xml")
-					if len(pf) != 1 {
+					pf := sdkLib.XmlPermissionsFile()
+					if pf == nil {
 						ctx.PropertyErrorf("java_libs", "%q failed to generate permission XML", depName)
 						return false
 					}
-					filesInfo = append(filesInfo, newApexFile(ctx, pf[0], pf[0].Base(), "etc/permissions", etc, nil))
+					filesInfo = append(filesInfo, newApexFile(ctx, pf, pf.Base(), "etc/permissions", etc, nil))
 					return true // track transitive dependencies
 				} else {
 					ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
diff --git a/apex/apex_test.go b/apex/apex_test.go
index c5b89e6..508bde6 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3488,8 +3488,9 @@
 		"etc/permissions/foo.xml",
 	})
 	// Permission XML should point to the activated path of impl jar of java_sdk_library
-	xml := ctx.ModuleForTests("foo", "android_common_myapex").Output("foo.xml")
-	ensureContains(t, xml.Args["content"], `<library name="foo" file="/apex/myapex/javalib/foo.jar"`)
+	sdkLibrary := ctx.ModuleForTests("foo", "android_common_myapex").Module().(*java.SdkLibrary)
+	xml := sdkLibrary.XmlPermissionsFileContent()
+	ensureContains(t, xml, `<library name="foo" file="/apex/myapex/javalib/foo.jar"`)
 }
 
 func TestCompatConfig(t *testing.T) {
diff --git a/apex/builder.go b/apex/builder.go
index 53f39a6..51818eb 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -508,13 +508,13 @@
 	// instead of `android.PathForOutput`) to return the correct path to the flattened
 	// APEX (as its contents is installed by Make, not Soong).
 	factx := flattenedApexContext{ctx}
-	apexName := proptools.StringDefault(a.properties.Apex_name, ctx.ModuleName())
-	a.outputFile = android.PathForModuleInstall(&factx, "apex", apexName)
+	apexBundleName := a.Name()
+	a.outputFile = android.PathForModuleInstall(&factx, "apex", apexBundleName)
 
 	if a.installable() && a.GetOverriddenBy() == "" {
-		installPath := android.PathForModuleInstall(ctx, "apex", apexName)
+		installPath := android.PathForModuleInstall(ctx, "apex", apexBundleName)
 		devicePath := android.InstallPathToOnDevicePath(ctx, installPath)
-		addFlattenedFileContextsInfos(ctx, apexName+":"+devicePath+":"+a.fileContexts.String())
+		addFlattenedFileContextsInfos(ctx, apexBundleName+":"+devicePath+":"+a.fileContexts.String())
 	}
 	a.buildFilesInfo(ctx)
 }
@@ -550,9 +550,9 @@
 		a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey", ".", etc, nil))
 
 		if a.properties.ApexType == flattenedApex {
-			apexName := proptools.StringDefault(a.properties.Apex_name, a.Name())
+			apexBundleName := a.Name()
 			for _, fi := range a.filesInfo {
-				dir := filepath.Join("apex", apexName, fi.installDir)
+				dir := filepath.Join("apex", apexBundleName, fi.installDir)
 				target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.builtFile.Base(), fi.builtFile)
 				for _, sym := range fi.symlinks {
 					ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 332cc45..c105954 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2751,3 +2751,42 @@
 		t.Errorf("libboth ar rule wanted %q, got %q", w, g)
 	}
 }
+
+func TestProductVariableDefaults(t *testing.T) {
+	bp := `
+		cc_defaults {
+			name: "libfoo_defaults",
+			srcs: ["foo.c"],
+			cppflags: ["-DFOO"],
+			product_variables: {
+				debuggable: {
+					cppflags: ["-DBAR"],
+				},
+			},
+		}
+
+		cc_library {
+			name: "libfoo",
+			defaults: ["libfoo_defaults"],
+		}
+	`
+
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.Debuggable = BoolPtr(true)
+
+	ctx := CreateTestContext()
+	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("variable", android.VariableMutator).Parallel()
+	})
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Module().(*Module)
+	if !android.InList("-DBAR", libfoo.flags.Local.CppFlags) {
+		t.Errorf("expected -DBAR in cppflags, got %q", libfoo.flags.Local.CppFlags)
+	}
+}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 2a929c5..720f79e 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -30,6 +30,8 @@
 
 	OnlyPreoptBootImageAndSystemServer bool // only preopt jars in the boot image or system server
 
+	UseArtImage bool // use the art image (use other boot class path dex files without image)
+
 	GenerateApexImage bool // generate an extra boot image only containing jars from the runtime apex
 	UseApexImage      bool // use the apex image by default
 
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index da68660..5faec08 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -106,8 +106,18 @@
 
 	global := dexpreoptGlobalConfig(ctx)
 	bootImage := defaultBootImageConfig(ctx)
+	dexFiles := bootImage.dexPathsDeps.Paths()
+	dexLocations := bootImage.dexLocationsDeps
+	if global.UseArtImage {
+		bootImage = artBootImageConfig(ctx)
+	}
 	if global.UseApexImage {
 		bootImage = frameworkJZBootImageConfig(ctx)
+		dexFiles = bootImage.dexPathsDeps.Paths()
+		dexLocations = bootImage.dexLocationsDeps
+		if global.UseArtImage {
+			bootImage = artJZBootImageConfig(ctx)
+		}
 	}
 
 	var archs []android.ArchType
@@ -178,8 +188,8 @@
 		DexPreoptImagesDeps:     imagesDeps,
 		DexPreoptImageLocations: bootImage.imageLocations,
 
-		PreoptBootClassPathDexFiles:     bootImage.dexPathsDeps.Paths(),
-		PreoptBootClassPathDexLocations: bootImage.dexLocationsDeps,
+		PreoptBootClassPathDexFiles:     dexFiles,
+		PreoptBootClassPathDexLocations: dexLocations,
 
 		PreoptExtractedApk: false,
 
diff --git a/java/java.go b/java/java.go
index c94ea82..dd44d06 100644
--- a/java/java.go
+++ b/java/java.go
@@ -37,14 +37,7 @@
 	RegisterJavaBuildComponents(android.InitRegistrationContext)
 
 	// Register sdk member types.
-	android.RegisterSdkMemberType(&headerLibrarySdkMemberType{
-		librarySdkMemberType{
-			android.SdkMemberTypeBase{
-				PropertyName: "java_header_libs",
-				SupportsSdk:  true,
-			},
-		},
-	})
+	android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType)
 
 	android.RegisterSdkMemberType(&implLibrarySdkMemberType{
 		librarySdkMemberType{
@@ -1849,6 +1842,15 @@
 	module.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
 }
 
+var javaHeaderLibsSdkMemberType android.SdkMemberType = &headerLibrarySdkMemberType{
+	librarySdkMemberType{
+		android.SdkMemberTypeBase{
+			PropertyName: "java_header_libs",
+			SupportsSdk:  true,
+		},
+	},
+}
+
 type headerLibrarySdkMemberType struct {
 	librarySdkMemberType
 }
diff --git a/java/sdk_library.go b/java/sdk_library.go
index fb8ae95..f1c565f 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/genrule"
 
 	"fmt"
 	"io"
@@ -264,17 +265,22 @@
 	}
 }
 
+var xmlPermissionsFileTag = dependencyTag{name: "xml-permissions-file"}
+
 func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
-	useBuiltStubs := !ctx.Config().UnbundledBuildUsePrebuiltSdks()
 	for _, apiScope := range module.getActiveApiScopes() {
 		// Add dependencies to the stubs library
-		if useBuiltStubs {
-			ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsName(apiScope))
-		}
+		ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsName(apiScope))
 
+		// And the api file
 		ctx.AddVariationDependencies(nil, apiScope.apiFileTag, module.docsName(apiScope))
 	}
 
+	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
+		// Add dependency to the rule for generating the xml permissions file
+		ctx.AddDependency(module, xmlPermissionsFileTag, module.genXmlPermissionsFileName())
+	}
+
 	module.Library.deps(ctx)
 }
 
@@ -284,8 +290,6 @@
 		module.Library.GenerateAndroidBuildActions(ctx)
 	}
 
-	module.buildPermissionsFile(ctx)
-
 	// Record the paths to the header jars of the library (stubs and impl).
 	// When this java_sdk_library is depended upon from others via "libs" property,
 	// the recorded paths will be returned depending on the link type of the caller.
@@ -310,33 +314,21 @@
 				ctx.ModuleErrorf("depends on module %q of unknown tag %q", otherName, tag)
 			}
 		}
+		if tag == xmlPermissionsFileTag {
+			if genRule, ok := to.(genrule.SourceFileGenerator); ok {
+				pf := genRule.GeneratedSourceFiles()
+				if len(pf) != 1 {
+					ctx.ModuleErrorf("%q failed to generate permission XML", otherName)
+				} else {
+					module.permissionsFile = pf[0]
+				}
+			} else {
+				ctx.ModuleErrorf("depends on module %q to generate xml permissions file but it does not provide any outputs", otherName)
+			}
+		}
 	})
 }
 
-func (module *SdkLibrary) buildPermissionsFile(ctx android.ModuleContext) {
-	xmlContent := fmt.Sprintf(permissionsTemplate, module.BaseModuleName(), module.implPath())
-	permissionsFile := android.PathForModuleOut(ctx, module.xmlFileName())
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.WriteFile,
-		Output:      permissionsFile,
-		Description: "Generating " + module.BaseModuleName() + " permissions",
-		Args: map[string]string{
-			"content": xmlContent,
-		},
-	})
-
-	module.permissionsFile = permissionsFile
-}
-
-func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case ".xml":
-		return android.Paths{module.permissionsFile}, nil
-	}
-	return module.Library.OutputFiles(tag)
-}
-
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
 	if proptools.Bool(module.sdkLibraryProperties.Api_only) {
 		return nil
@@ -423,6 +415,11 @@
 	return module.BaseModuleName() + sdkXmlFileSuffix
 }
 
+// Module name of the rule for generating the XML permissions file
+func (module *SdkLibrary) genXmlPermissionsFileName() string {
+	return "gen-" + module.BaseModuleName() + sdkXmlFileSuffix
+}
+
 // Get the sdk version for use when compiling the stubs library.
 func (module *SdkLibrary) sdkVersionForStubsLibrary(mctx android.LoadHookContext, apiScope *apiScope) string {
 	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
@@ -466,9 +463,6 @@
 		Compile_dex         *bool
 		Java_version        *string
 		Product_variables   struct {
-			Unbundled_build struct {
-				Enabled *bool
-			}
 			Pdk struct {
 				Enabled *bool
 			}
@@ -487,10 +481,6 @@
 	props.System_modules = module.Library.Module.deviceProperties.System_modules
 	props.Installable = proptools.BoolPtr(false)
 	props.Libs = module.sdkLibraryProperties.Stub_only_libs
-	// Unbundled apps will use the prebult one from /prebuilts/sdk
-	if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
-		props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false)
-	}
 	props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
 	props.Openjdk9.Srcs = module.Library.Module.properties.Openjdk9.Srcs
 	props.Openjdk9.Javacflags = module.Library.Module.properties.Openjdk9.Javacflags
@@ -623,8 +613,34 @@
 	mctx.CreateModule(DroidstubsFactory, &props)
 }
 
+func (module *SdkLibrary) XmlPermissionsFile() android.Path {
+	return module.permissionsFile
+}
+
+func (module *SdkLibrary) XmlPermissionsFileContent() string {
+	return fmt.Sprintf(permissionsTemplate, module.BaseModuleName(), module.implPath())
+}
+
 // Creates the xml file that publicizes the runtime library
 func (module *SdkLibrary) createXmlFile(mctx android.LoadHookContext) {
+
+	xmlContent := module.XmlPermissionsFileContent()
+
+	genRuleName := module.genXmlPermissionsFileName()
+
+	// Create a genrule module to create the XML permissions file.
+	genRuleProps := struct {
+		Name *string
+		Cmd  *string
+		Out  []string
+	}{
+		Name: proptools.StringPtr(genRuleName),
+		Cmd:  proptools.StringPtr("echo -e '" + xmlContent + "' > '$(out)'"),
+		Out:  []string{module.xmlFileName()},
+	}
+
+	mctx.CreateModule(genrule.GenRuleFactory, &genRuleProps)
+
 	// creates a prebuilt_etc module to actually place the xml file under
 	// <partition>/etc/permissions
 	etcProps := struct {
@@ -637,7 +653,7 @@
 		System_ext_specific *bool
 	}{}
 	etcProps.Name = proptools.StringPtr(module.xmlFileName())
-	etcProps.Src = proptools.StringPtr(":" + module.BaseModuleName() + "{.xml}")
+	etcProps.Src = proptools.StringPtr(":" + genRuleName)
 	etcProps.Sub_dir = proptools.StringPtr("permissions")
 	if module.SocSpecific() {
 		etcProps.Soc_specific = proptools.BoolPtr(true)
@@ -651,7 +667,7 @@
 	mctx.CreateModule(android.PrebuiltEtcFactory, &etcProps)
 }
 
-func (module *SdkLibrary) PrebuiltJars(ctx android.BaseModuleContext, s sdkSpec) android.Paths {
+func PrebuiltJars(ctx android.BaseModuleContext, baseName string, s sdkSpec) android.Paths {
 	var ver sdkVersion
 	var kind sdkKind
 	if s.usePrebuilt(ctx) {
@@ -665,7 +681,7 @@
 	}
 
 	dir := filepath.Join("prebuilts", "sdk", ver.String(), kind.String())
-	jar := filepath.Join(dir, module.BaseModuleName()+".jar")
+	jar := filepath.Join(dir, baseName+".jar")
 	jarPath := android.ExistentPathForSource(ctx, jar)
 	if !jarPath.Valid() {
 		if ctx.Config().AllowMissingDependencies() {
@@ -683,10 +699,9 @@
 	sdkVersion sdkSpec,
 	headerJars bool) android.Paths {
 
-	// If a specific numeric version has been requested or the build is explicitly configured
-	// for it then use prebuilt versions of the sdk.
-	if sdkVersion.version.isNumbered() || ctx.Config().UnbundledBuildUsePrebuiltSdks() {
-		return module.PrebuiltJars(ctx, sdkVersion)
+	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
+	if sdkVersion.version.isNumbered() {
+		return PrebuiltJars(ctx, module.BaseModuleName(), sdkVersion)
 	} else {
 		if !sdkVersion.specified() {
 			if headerJars {
@@ -899,6 +914,11 @@
 
 func (module *sdkLibraryImport) createInternalModules(mctx android.LoadHookContext) {
 
+	// If the build is configured to use prebuilts then force this to be preferred.
+	if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
+		module.prebuilt.ForcePrefer()
+	}
+
 	for apiScope, scopeProperties := range module.scopeProperties() {
 		if len(scopeProperties.Jars) == 0 {
 			continue
@@ -914,6 +934,7 @@
 			Sdk_version         *string
 			Libs                []string
 			Jars                []string
+			Prefer              *bool
 		}{}
 
 		props.Name = proptools.StringPtr(apiScope.stubsModuleName(module.BaseModuleName()))
@@ -933,6 +954,12 @@
 			props.System_ext_specific = proptools.BoolPtr(true)
 		}
 
+		// If the build should use prebuilt sdks then set prefer to true on the stubs library.
+		// That will cause the prebuilt version of the stubs to override the source version.
+		if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
+			props.Prefer = proptools.BoolPtr(true)
+		}
+
 		mctx.CreateModule(ImportFactory, &props)
 	}
 
@@ -980,6 +1007,11 @@
 	ctx android.BaseModuleContext,
 	sdkVersion sdkSpec) android.Paths {
 
+	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
+	if sdkVersion.version.isNumbered() {
+		return PrebuiltJars(ctx, module.BaseModuleName(), sdkVersion)
+	}
+
 	var apiScope *apiScope
 	switch sdkVersion.kind {
 	case sdkSystem:
diff --git a/java/system_modules.go b/java/system_modules.go
index 92297c4..731503f 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -31,6 +31,15 @@
 	RegisterSystemModulesBuildComponents(android.InitRegistrationContext)
 
 	pctx.SourcePathVariable("moduleInfoJavaPath", "build/soong/scripts/jars-to-module-info-java.sh")
+
+	// Register sdk member types.
+	android.RegisterSdkMemberType(&systemModulesSdkMemberType{
+		android.SdkMemberTypeBase{
+			PropertyName:         "java_system_modules",
+			SupportsSdk:          true,
+			TransitiveSdkMembers: true,
+		},
+	})
 }
 
 func RegisterSystemModulesBuildComponents(ctx android.RegistrationContext) {
@@ -66,6 +75,10 @@
 		},
 	},
 		"classpath", "outDir", "workDir")
+
+	// Dependency tag that causes the added dependencies to be added as java_header_libs
+	// to the sdk/module_exports/snapshot.
+	systemModulesLibsTag = android.DependencyTagForSdkMemberType(javaHeaderLibsSdkMemberType)
 )
 
 func TransformJarsToSystemModules(ctx android.ModuleContext, jars android.Paths) (android.Path, android.Paths) {
@@ -107,6 +120,7 @@
 type SystemModules struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.SdkBase
 
 	properties SystemModulesProperties
 
@@ -125,7 +139,7 @@
 func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	var jars android.Paths
 
-	ctx.VisitDirectDepsWithTag(libTag, func(module android.Module) {
+	ctx.VisitDirectDepsWithTag(systemModulesLibsTag, func(module android.Module) {
 		dep, _ := module.(Dependency)
 		jars = append(jars, dep.HeaderJars()...)
 	})
@@ -136,7 +150,7 @@
 }
 
 func (system *SystemModules) DepsMutator(ctx android.BottomUpMutatorContext) {
-	ctx.AddVariationDependencies(nil, libTag, system.properties.Libs...)
+	ctx.AddVariationDependencies(nil, systemModulesLibsTag, system.properties.Libs...)
 }
 
 func (system *SystemModules) AndroidMk() android.AndroidMkData {
@@ -173,6 +187,7 @@
 	android.InitPrebuiltModule(module, &module.properties.Libs)
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	android.InitSdkAwareModule(module)
 	return module
 }
 
@@ -188,3 +203,37 @@
 func (system *systemModulesImport) Prebuilt() *android.Prebuilt {
 	return &system.prebuilt
 }
+
+type systemModulesSdkMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (mt *systemModulesSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (mt *systemModulesSdkMemberType) IsInstance(module android.Module) bool {
+	if _, ok := module.(*SystemModules); ok {
+		// A prebuilt system module cannot be added as a member of an sdk because the source and
+		// snapshot instances would conflict.
+		_, ok := module.(*systemModulesImport)
+		return !ok
+	}
+	return false
+}
+
+func (mt *systemModulesSdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
+	variants := member.Variants()
+	if len(variants) != 1 {
+		sdkModuleContext.ModuleErrorf("sdk contains %d variants of member %q but only one is allowed", len(variants), member.Name())
+		for _, variant := range variants {
+			sdkModuleContext.ModuleErrorf("    %q", variant)
+		}
+	}
+	variant := variants[0]
+	systemModule := variant.(*SystemModules)
+
+	pbm := builder.AddPrebuiltModule(member, "java_system_modules_import")
+	// Add the references to the libraries that form the system module.
+	pbm.AddPropertyWithTag("libs", systemModule.properties.Libs, builder.SdkMemberReferencePropertyTag())
+}
diff --git a/rust/rust.go b/rust/rust.go
index e2af6f0..e4f85f0 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -705,13 +705,15 @@
 func linkPathFromFilePath(filepath android.Path) string {
 	return strings.Split(filepath.String(), filepath.Base())[0]
 }
+
 func libNameFromFilePath(filepath android.Path) string {
-	libName := strings.Split(filepath.Base(), filepath.Ext())[0]
+	libName := strings.TrimSuffix(filepath.Base(), filepath.Ext())
 	if strings.HasPrefix(libName, "lib") {
 		libName = libName[3:]
 	}
 	return libName
 }
+
 func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	ctx := &depsContext{
 		BottomUpMutatorContext: actx,
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 3be9ee7..afe530a 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -114,13 +114,13 @@
 
 // Test that we can extract the lib name from a lib path.
 func TestLibNameFromFilePath(t *testing.T) {
-	libBarPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so")
+	libBarPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so.so")
 	libLibPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/liblib.dylib.so")
 
 	libBarName := libNameFromFilePath(libBarPath)
 	libLibName := libNameFromFilePath(libLibPath)
 
-	expectedResult := "bar"
+	expectedResult := "bar.so"
 	if libBarName != expectedResult {
 		t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libBarName)
 	}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index cc893b9..79d3c26 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -583,3 +583,136 @@
 		checkMergeZip(".intermediates/myexports/linux_glibc_common/tmp/java/myjavaapistubs_stubs_sources.zip"),
 	)
 }
+
+func TestSnapshotWithJavaSystemModules(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			java_system_modules: ["my-system-modules"],
+		}
+
+		java_system_modules {
+			name: "my-system-modules",
+			libs: ["system-module"],
+		}
+
+		java_library {
+			name: "system-module",
+			srcs: ["Test.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_system-module@current",
+    sdk_member_name: "system-module",
+    jars: ["java/system-module.jar"],
+}
+
+java_import {
+    name: "system-module",
+    prefer: false,
+    jars: ["java/system-module.jar"],
+}
+
+java_system_modules_import {
+    name: "mysdk_my-system-modules@current",
+    sdk_member_name: "my-system-modules",
+    libs: ["mysdk_system-module@current"],
+}
+
+java_system_modules_import {
+    name: "my-system-modules",
+    prefer: false,
+    libs: ["system-module"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    java_system_modules: ["mysdk_my-system-modules@current"],
+}
+`),
+		checkAllCopyRules(".intermediates/system-module/android_common/turbine-combined/system-module.jar -> java/system-module.jar"),
+	)
+}
+
+func TestHostSnapshotWithJavaSystemModules(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			device_supported: false,
+			host_supported: true,
+			java_system_modules: ["my-system-modules"],
+		}
+
+		java_system_modules {
+			name: "my-system-modules",
+			device_supported: false,
+			host_supported: true,
+			libs: ["system-module"],
+		}
+
+		java_library {
+			name: "system-module",
+			device_supported: false,
+			host_supported: true,
+			srcs: ["Test.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_system-module@current",
+    sdk_member_name: "system-module",
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/system-module.jar"],
+}
+
+java_import {
+    name: "system-module",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/system-module.jar"],
+}
+
+java_system_modules_import {
+    name: "mysdk_my-system-modules@current",
+    sdk_member_name: "my-system-modules",
+    device_supported: false,
+    host_supported: true,
+    libs: ["mysdk_system-module@current"],
+}
+
+java_system_modules_import {
+    name: "my-system-modules",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    libs: ["system-module"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    device_supported: false,
+    host_supported: true,
+    java_system_modules: ["mysdk_my-system-modules@current"],
+}
+`),
+		checkAllCopyRules(".intermediates/system-module/linux_glibc_common/javac/system-module.jar -> java/system-module.jar"),
+	)
+}
diff --git a/sdk/testing.go b/sdk/testing.go
index c9cc30f..6102441 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -72,6 +72,7 @@
 	java.RegisterJavaBuildComponents(ctx)
 	java.RegisterAppBuildComponents(ctx)
 	java.RegisterStubsBuildComponents(ctx)
+	java.RegisterSystemModulesBuildComponents(ctx)
 
 	// from cc package
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
diff --git a/sdk/update.go b/sdk/update.go
index 97bafa1..9032d1f 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -105,23 +105,23 @@
 // Collect all the members.
 //
 // The members are first grouped by type and then grouped by name. The order of
-// the types is the order they are referenced in android.SdkMemberTypes. The
-// names are in order in which the dependencies were added.
+// the types is the order they are referenced in android.SdkMemberTypesRegistry.
+// The names are in the order in which the dependencies were added.
 func (s *sdk) collectMembers(ctx android.ModuleContext) []*sdkMember {
 	byType := make(map[android.SdkMemberType][]*sdkMember)
 	byName := make(map[string]*sdkMember)
 
-	ctx.VisitDirectDeps(func(m android.Module) {
-		tag := ctx.OtherModuleDependencyTag(m)
+	ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
+		tag := ctx.OtherModuleDependencyTag(child)
 		if memberTag, ok := tag.(android.SdkMemberTypeDependencyTag); ok {
 			memberType := memberTag.SdkMemberType()
 
 			// Make sure that the resolved module is allowed in the member list property.
-			if !memberType.IsInstance(m) {
-				ctx.ModuleErrorf("module %q is not valid in property %s", ctx.OtherModuleName(m), memberType.SdkPropertyName())
+			if !memberType.IsInstance(child) {
+				ctx.ModuleErrorf("module %q is not valid in property %s", ctx.OtherModuleName(child), memberType.SdkPropertyName())
 			}
 
-			name := ctx.OtherModuleName(m)
+			name := ctx.OtherModuleName(child)
 
 			member := byName[name]
 			if member == nil {
@@ -130,8 +130,14 @@
 				byType[memberType] = append(byType[memberType], member)
 			}
 
-			member.variants = append(member.variants, m.(android.SdkAware))
+			member.variants = append(member.variants, child.(android.SdkAware))
+
+			// If the member type supports transitive sdk members then recurse down into
+			// its dependencies, otherwise exit traversal.
+			return memberType.HasTransitiveSdkMembers()
 		}
+
+		return false
 	})
 
 	var members []*sdkMember
@@ -285,13 +291,17 @@
 	return outputZipFile
 }
 
+type propertyTag struct {
+	name string
+}
+
+var sdkMemberReferencePropertyTag = propertyTag{"sdkMemberReferencePropertyTag"}
+
 type unversionedToVersionedTransformation struct {
 	identityTransformation
 	builder *snapshotBuilder
 }
 
-var _ bpTransformer = (*unversionedToVersionedTransformation)(nil)
-
 func (t unversionedToVersionedTransformation) transformModule(module *bpModule) *bpModule {
 	// Use a versioned name for the module but remember the original name for the
 	// snapshot.
@@ -301,6 +311,14 @@
 	return module
 }
 
+func (t unversionedToVersionedTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+	if tag == sdkMemberReferencePropertyTag {
+		return t.builder.versionedSdkMemberNames(value.([]string)), tag
+	} else {
+		return value, tag
+	}
+}
+
 func generateBpContents(contents *generatedContents, bpFile *bpFile) {
 	contents.Printfln("// This is auto-generated. DO NOT EDIT.")
 	for _, bpModule := range bpFile.order {
@@ -447,6 +465,10 @@
 	}
 }
 
+func (s *snapshotBuilder) SdkMemberReferencePropertyTag() android.BpPropertyTag {
+	return sdkMemberReferencePropertyTag
+}
+
 // Get a versioned name appropriate for the SDK snapshot version being taken.
 func (s *snapshotBuilder) versionedSdkMemberName(unversionedName string) string {
 	return versionedSdkMemberName(s.ctx, unversionedName, s.version)