[automerger skipped] Use implementation jar for updatable-media in snapshot for S am: 8a61b59e17 -s ours

am skip reason: Merged-In Ib49484d00a60b4ed7f8268e04f9c10a3498edb56 with SHA-1 be75059999 is already in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/build/soong/+/19290569

Change-Id: If723e19d906332e2cb4a81e0167ddd4b9227ebec
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/android/defaults.go b/android/defaults.go
index 8b121f6..54f44bc 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"bytes"
+	"fmt"
 	"reflect"
 
 	"github.com/google/blueprint"
@@ -67,9 +69,11 @@
 	// Set the property structures into which defaults will be added.
 	setProperties(props []interface{}, variableProperties interface{})
 
-	// Apply defaults from the supplied Defaults to the property structures supplied to
+	// Apply defaults from the supplied DefaultsModule to the property structures supplied to
 	// setProperties(...).
-	applyDefaults(TopDownMutatorContext, []Defaults)
+	applyDefaults(TopDownMutatorContext, []DefaultsModule)
+
+	applySingleDefaultsWithTracker(EarlyModuleContext, DefaultsModule, defaultsTrackerFunc)
 
 	// Set the hook to be called after any defaults have been applied.
 	//
@@ -115,9 +119,23 @@
 	Defaults_visibility []string
 }
 
+// AdditionalDefaultsProperties contains properties of defaults modules which
+// can have other defaults applied.
+type AdditionalDefaultsProperties struct {
+
+	// The list of properties set by the default whose values must not be changed by any module that
+	// applies these defaults. It is an error if a property is not supported by the defaults module or
+	// has not been set to a non-zero value. If this contains "*" then that must be the only entry in
+	// which case all properties that are set on this defaults will be protected (except the
+	// protected_properties and visibility.
+	Protected_properties []string
+}
+
 type DefaultsModuleBase struct {
 	DefaultableModuleBase
 
+	defaultsProperties AdditionalDefaultsProperties
+
 	// Included to support setting bazel_module.label for multiple Soong modules to the same Bazel
 	// target. This is primarily useful for modules that were architecture specific and instead are
 	// handled in Bazel as a select().
@@ -151,6 +169,18 @@
 	// DefaultsModuleBase will type-assert to the Defaults interface.
 	isDefaults() bool
 
+	// additionalDefaultableProperties returns additional properties provided by the defaults which
+	// can themselves have defaults applied.
+	additionalDefaultableProperties() []interface{}
+
+	// protectedProperties returns the names of the properties whose values cannot be changed by a
+	// module that applies these defaults.
+	protectedProperties() []string
+
+	// setProtectedProperties sets the names of the properties whose values cannot be changed by a
+	// module that applies these defaults.
+	setProtectedProperties(protectedProperties []string)
+
 	// Get the structures containing the properties for which defaults can be provided.
 	properties() []interface{}
 
@@ -167,6 +197,18 @@
 	Bazelable
 }
 
+func (d *DefaultsModuleBase) additionalDefaultableProperties() []interface{} {
+	return []interface{}{&d.defaultsProperties}
+}
+
+func (d *DefaultsModuleBase) protectedProperties() []string {
+	return d.defaultsProperties.Protected_properties
+}
+
+func (d *DefaultsModuleBase) setProtectedProperties(protectedProperties []string) {
+	d.defaultsProperties.Protected_properties = protectedProperties
+}
+
 func (d *DefaultsModuleBase) properties() []interface{} {
 	return d.defaultableProperties
 }
@@ -190,6 +232,10 @@
 		&ApexProperties{},
 		&distProperties{})
 
+	// Additional properties of defaults modules that can themselves have
+	// defaults applied.
+	module.AddProperties(module.additionalDefaultableProperties()...)
+
 	// Bazel module must be initialized _before_ Defaults to be included in cc_defaults module.
 	InitBazelModule(module)
 	initAndroidModuleBase(module)
@@ -218,6 +264,57 @@
 	// The applicable licenses property for defaults is 'licenses'.
 	setPrimaryLicensesProperty(module, "licenses", &commonProperties.Licenses)
 
+	AddLoadHook(module, func(ctx LoadHookContext) {
+
+		protectedProperties := module.protectedProperties()
+		if len(protectedProperties) == 0 {
+			return
+		}
+
+		propertiesAvailable := map[string]struct{}{}
+		propertiesSet := map[string]struct{}{}
+
+		// A defaults tracker which will keep track of which properties have been set on this module.
+		collector := func(defaults DefaultsModule, property string, dstValue interface{}, srcValue interface{}) bool {
+			value := reflect.ValueOf(dstValue)
+			propertiesAvailable[property] = struct{}{}
+			if !value.IsZero() {
+				propertiesSet[property] = struct{}{}
+			}
+			// Skip all the properties so that there are no changes to the defaults.
+			return false
+		}
+
+		// Try and apply this module's defaults to itself, so that the properties can be collected but
+		// skip all the properties so it doesn't actually do anything.
+		module.applySingleDefaultsWithTracker(ctx, module, collector)
+
+		if InList("*", protectedProperties) {
+			if len(protectedProperties) != 1 {
+				ctx.PropertyErrorf("protected_properties", `if specified then "*" must be the only property listed`)
+				return
+			}
+
+			// Do not automatically protect the protected_properties property.
+			delete(propertiesSet, "protected_properties")
+
+			// Or the visibility property.
+			delete(propertiesSet, "visibility")
+
+			// Replace the "*" with the names of all the properties that have been set.
+			protectedProperties = SortedStringKeys(propertiesSet)
+			module.setProtectedProperties(protectedProperties)
+		} else {
+			for _, property := range protectedProperties {
+				if _, ok := propertiesAvailable[property]; !ok {
+					ctx.PropertyErrorf(property, "property is not supported by this module type %q",
+						ctx.ModuleType())
+				} else if _, ok := propertiesSet[property]; !ok {
+					ctx.PropertyErrorf(property, "is not set; protected properties must be explicitly set")
+				}
+			}
+		}
+	})
 }
 
 var _ Defaults = (*DefaultsModuleBase)(nil)
@@ -269,35 +366,204 @@
 	b.setNamespacedVariableProps(dst)
 }
 
+// defaultValueInfo contains information about each default value that applies to a protected
+// property.
+type defaultValueInfo struct {
+	// The DefaultsModule providing the value, which may be defined on that module or applied as a
+	// default from other modules.
+	module Module
+
+	// The default value, as returned by getComparableValue
+	defaultValue reflect.Value
+}
+
+// protectedPropertyInfo contains information about each property that has to be protected when
+// applying defaults.
+type protectedPropertyInfo struct {
+	// True if the property was set on the module to which defaults are applied, this is an error.
+	propertySet bool
+
+	// The original value of the property on the module, as returned by getComparableValue.
+	originalValue reflect.Value
+
+	// A list of defaults for the property that are being applied.
+	defaultValues []defaultValueInfo
+}
+
+// getComparableValue takes a reflect.Value that may be a pointer to another value and returns a
+// reflect.Value to the underlying data or the original if was not a pointer or was nil. The
+// returned values can then be compared for equality.
+func getComparableValue(value reflect.Value) reflect.Value {
+	if value.IsZero() {
+		return value
+	}
+	for value.Kind() == reflect.Ptr {
+		value = value.Elem()
+	}
+	return value
+}
+
 func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext,
-	defaultsList []Defaults) {
+	defaultsList []DefaultsModule) {
+
+	// Collate information on all the properties protected by each of the default modules applied
+	// to this module.
+	allProtectedProperties := map[string]*protectedPropertyInfo{}
+	for _, defaults := range defaultsList {
+		for _, property := range defaults.protectedProperties() {
+			info := allProtectedProperties[property]
+			if info == nil {
+				info = &protectedPropertyInfo{}
+				allProtectedProperties[property] = info
+			}
+		}
+	}
+
+	// If there are any protected properties then collate information about attempts to change them.
+	var protectedPropertyInfoCollector defaultsTrackerFunc
+	if len(allProtectedProperties) > 0 {
+		protectedPropertyInfoCollector = func(defaults DefaultsModule, property string,
+			dstValue interface{}, srcValue interface{}) bool {
+
+			// If the property is not protected then return immediately.
+			info := allProtectedProperties[property]
+			if info == nil {
+				return true
+			}
+
+			currentValue := reflect.ValueOf(dstValue)
+			if info.defaultValues == nil {
+				info.propertySet = !currentValue.IsZero()
+				info.originalValue = getComparableValue(currentValue)
+			}
+
+			defaultValue := reflect.ValueOf(srcValue)
+			if !defaultValue.IsZero() {
+				info.defaultValues = append(info.defaultValues,
+					defaultValueInfo{defaults, getComparableValue(defaultValue)})
+			}
+
+			return true
+		}
+	}
 
 	for _, defaults := range defaultsList {
 		if ctx.Config().runningAsBp2Build {
 			applyNamespacedVariableDefaults(defaults, ctx)
 		}
-		for _, prop := range defaultable.defaultableProperties {
-			if prop == defaultable.defaultableVariableProperties {
-				defaultable.applyDefaultVariableProperties(ctx, defaults, prop)
-			} else {
-				defaultable.applyDefaultProperties(ctx, defaults, prop)
+
+		defaultable.applySingleDefaultsWithTracker(ctx, defaults, protectedPropertyInfoCollector)
+	}
+
+	// Check the status of any protected properties.
+	for property, info := range allProtectedProperties {
+		if len(info.defaultValues) == 0 {
+			// No defaults were applied to the protected properties. Possibly because this module type
+			// does not support any of them.
+			continue
+		}
+
+		// Check to make sure that there are no conflicts between the defaults.
+		conflictingDefaults := false
+		previousDefaultValue := reflect.ValueOf(false)
+		for _, defaultInfo := range info.defaultValues {
+			defaultValue := defaultInfo.defaultValue
+			if previousDefaultValue.IsZero() {
+				previousDefaultValue = defaultValue
+			} else if !reflect.DeepEqual(previousDefaultValue.Interface(), defaultValue.Interface()) {
+				conflictingDefaults = true
+				break
 			}
 		}
+
+		if conflictingDefaults {
+			var buf bytes.Buffer
+			for _, defaultInfo := range info.defaultValues {
+				buf.WriteString(fmt.Sprintf("\n    defaults module %q provides value %#v",
+					ctx.OtherModuleName(defaultInfo.module), defaultInfo.defaultValue))
+			}
+			result := buf.String()
+			ctx.ModuleErrorf("has conflicting default values for protected property %q:%s", property, result)
+			continue
+		}
+
+		// Now check to see whether there the current module tried to override/append to the defaults.
+		if info.propertySet {
+			originalValue := info.originalValue
+			// Just compare against the first defaults.
+			defaultValue := info.defaultValues[0].defaultValue
+			defaults := info.defaultValues[0].module
+
+			if originalValue.Kind() == reflect.Slice {
+				ctx.ModuleErrorf("attempts to append %q to protected property %q's value of %q defined in module %q",
+					originalValue,
+					property,
+					defaultValue,
+					ctx.OtherModuleName(defaults))
+			} else {
+				same := reflect.DeepEqual(originalValue.Interface(), defaultValue.Interface())
+				message := ""
+				if same {
+					message = fmt.Sprintf(" with a matching value (%#v) so this property can simply be removed.", originalValue)
+				} else {
+					message = fmt.Sprintf(" with a different value (override %#v with %#v) so removing the property may necessitate other changes.", defaultValue, originalValue)
+				}
+				ctx.ModuleErrorf("attempts to override protected property %q defined in module %q%s",
+					property,
+					ctx.OtherModuleName(defaults), message)
+			}
+		}
+	}
+}
+
+func (defaultable *DefaultableModuleBase) applySingleDefaultsWithTracker(ctx EarlyModuleContext, defaults DefaultsModule, tracker defaultsTrackerFunc) {
+	for _, prop := range defaultable.defaultableProperties {
+		var err error
+		if prop == defaultable.defaultableVariableProperties {
+			err = defaultable.applyDefaultVariableProperties(defaults, prop, tracker)
+		} else {
+			err = defaultable.applyDefaultProperties(defaults, prop, tracker)
+		}
+		if err != nil {
+			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+				ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+			} else {
+				panic(err)
+			}
+		}
+	}
+}
+
+// defaultsTrackerFunc is the type of a function that can be used to track how defaults are applied.
+type defaultsTrackerFunc func(defaults DefaultsModule, property string,
+	dstValue interface{}, srcValue interface{}) bool
+
+// filterForTracker wraps a defaultsTrackerFunc in a proptools.ExtendPropertyFilterFunc
+func filterForTracker(defaults DefaultsModule, tracker defaultsTrackerFunc) proptools.ExtendPropertyFilterFunc {
+	if tracker == nil {
+		return nil
+	}
+	return func(property string,
+		dstField, srcField reflect.StructField,
+		dstValue, srcValue interface{}) (bool, error) {
+
+		apply := tracker(defaults, property, dstValue, srcValue)
+		return apply, nil
 	}
 }
 
 // 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{}) {
+func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(defaults DefaultsModule,
+	defaultableProp interface{}, tracker defaultsTrackerFunc) error {
 	if defaultableProp == nil {
-		return
+		return nil
 	}
 
 	defaultsProp := defaults.productVariableProperties()
 	if defaultsProp == nil {
-		return
+		return nil
 	}
 
 	dst := []interface{}{
@@ -307,31 +573,26 @@
 		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)
-		}
-	}
+	filter := filterForTracker(defaults, tracker)
+
+	return proptools.PrependMatchingProperties(dst, defaultsProp, filter)
 }
 
-func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx TopDownMutatorContext,
-	defaults Defaults, defaultableProp interface{}) {
+func (defaultable *DefaultableModuleBase) applyDefaultProperties(defaults DefaultsModule,
+	defaultableProp interface{}, checker defaultsTrackerFunc) error {
+
+	filter := filterForTracker(defaults, checker)
 
 	for _, def := range defaults.properties() {
 		if proptools.TypeEqual(defaultableProp, def) {
-			err := proptools.PrependProperties(defaultableProp, def, nil)
+			err := proptools.PrependProperties(defaultableProp, def, filter)
 			if err != nil {
-				if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-					ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
-				} else {
-					panic(err)
-				}
+				return err
 			}
 		}
 	}
+
+	return nil
 }
 
 func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) {
@@ -348,12 +609,12 @@
 func defaultsMutator(ctx TopDownMutatorContext) {
 	if defaultable, ok := ctx.Module().(Defaultable); ok {
 		if len(defaultable.defaults().Defaults) > 0 {
-			var defaultsList []Defaults
+			var defaultsList []DefaultsModule
 			seen := make(map[Defaults]bool)
 
 			ctx.WalkDeps(func(module, parent Module) bool {
 				if ctx.OtherModuleDependencyTag(module) == DefaultsDepTag {
-					if defaults, ok := module.(Defaults); ok {
+					if defaults, ok := module.(DefaultsModule); ok {
 						if !seen[defaults] {
 							seen[defaults] = true
 							defaultsList = append(defaultsList, defaults)
diff --git a/android/defaults_test.go b/android/defaults_test.go
index a7542ab..d80f40c 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -19,7 +19,14 @@
 )
 
 type defaultsTestProperties struct {
-	Foo []string
+	Foo    []string
+	Bar    []string
+	Nested struct {
+		Fizz *bool
+	}
+	Other struct {
+		Buzz *string
+	}
 }
 
 type defaultsTestModule struct {
@@ -130,3 +137,167 @@
 	// TODO: missing transitive defaults is currently not handled
 	_ = missingTransitiveDefaults
 }
+
+func TestProtectedProperties_ProtectedPropertyNotSet(t *testing.T) {
+	bp := `
+		defaults {
+			name: "transitive",
+			protected_properties: ["foo"],
+		}
+	`
+
+	GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
+		"module \"transitive\": foo: is not set; protected properties must be explicitly set")).
+		RunTest(t)
+}
+
+func TestProtectedProperties_ProtectedPropertyNotLeaf(t *testing.T) {
+	bp := `
+		defaults {
+			name: "transitive",
+			protected_properties: ["nested"],
+			nested: {
+				fizz: true,
+			},
+		}
+	`
+
+	GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
+		`\Qmodule "transitive": nested: property is not supported by this module type "defaults"\E`)).
+		RunTest(t)
+}
+
+// TestProtectedProperties_ApplyDefaults makes sure that the protected_properties property has
+// defaults applied.
+func TestProtectedProperties_HasDefaultsApplied(t *testing.T) {
+
+	bp := `
+		defaults {
+			name: "transitive",
+			protected_properties: ["foo"],
+			foo: ["transitive"],
+		}
+
+		defaults {
+			name: "defaults",
+			defaults: ["transitive"],
+			protected_properties: ["bar"],
+			bar: ["defaults"],
+		}
+	`
+
+	result := GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
+
+	defaults := result.Module("defaults", "").(DefaultsModule)
+	AssertDeepEquals(t, "defaults protected properties", []string{"foo", "bar"}, defaults.protectedProperties())
+}
+
+// TestProtectedProperties_ProtectAllProperties makes sure that protected_properties: ["*"] protects
+// all properties.
+func TestProtectedProperties_ProtectAllProperties(t *testing.T) {
+
+	bp := `
+		defaults {
+			name: "transitive",
+			protected_properties: ["other.buzz"],
+			other: {
+				buzz: "transitive",
+			},
+		}
+
+		defaults {
+			name: "defaults",
+			defaults: ["transitive"],
+			visibility: ["//visibility:private"],
+			protected_properties: ["*"],
+			foo: ["other"],
+			bar: ["defaults"],
+			nested: {
+				fizz: true,
+			}
+		}
+	`
+
+	result := GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
+
+	defaults := result.Module("defaults", "").(DefaultsModule)
+	AssertDeepEquals(t, "defaults protected properties", []string{"other.buzz", "bar", "foo", "nested.fizz"},
+		defaults.protectedProperties())
+}
+
+func TestProtectedProperties_DetectedOverride(t *testing.T) {
+	bp := `
+		defaults {
+			name: "defaults",
+			protected_properties: ["foo", "nested.fizz"],
+			foo: ["defaults"],
+			nested: {
+				fizz: true,
+			},
+		}
+
+		test {
+			name: "foo",
+			defaults: ["defaults"],
+			foo: ["module"],
+			nested: {
+				fizz: false,
+			},
+		}
+	`
+
+	GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(
+		[]string{
+			`\Qmodule "foo": attempts to append ["module"] to protected property "foo"'s value of ["defaults"] defined in module "defaults"\E`,
+			`\Qmodule "foo": attempts to override protected property "nested.fizz" defined in module "defaults" with a different value (override true with false) so removing the property may necessitate other changes.\E`,
+		})).RunTest(t)
+}
+
+func TestProtectedProperties_DefaultsConflict(t *testing.T) {
+	bp := `
+		defaults {
+			name: "defaults1",
+			protected_properties: ["other.buzz"],
+			other: {
+				buzz: "value",
+			},
+		}
+
+		defaults {
+			name: "defaults2",
+			protected_properties: ["other.buzz"],
+			other: {
+				buzz: "another",
+			},
+		}
+
+		test {
+			name: "foo",
+			defaults: ["defaults1", "defaults2"],
+		}
+	`
+
+	GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
+		`\Qmodule "foo": has conflicting default values for protected property "other.buzz":
+    defaults module "defaults1" provides value "value"
+    defaults module "defaults2" provides value "another"\E`,
+	)).RunTest(t)
+}
diff --git a/android/sdk.go b/android/sdk.go
index 1ce692f..a71f7f2 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -674,6 +674,13 @@
 	// host OS variant explicitly and disable all other host OS'es.
 	IsHostOsDependent() bool
 
+	// SupportedLinkages returns the names of the linkage variants supported by this module.
+	SupportedLinkages() []string
+
+	// ArePrebuiltsRequired returns true if prebuilts are required in the sdk snapshot, false
+	// otherwise.
+	ArePrebuiltsRequired() bool
+
 	// AddDependencies adds dependencies from the SDK module to all the module variants the member
 	// type contributes to the SDK. `names` is the list of module names given in the member type
 	// property (as returned by SdkPropertyName()) in the SDK module. The exact set of variants
@@ -737,6 +744,9 @@
 
 	// SupportedTraits returns the set of traits supported by this member type.
 	SupportedTraits() SdkMemberTraitSet
+
+	// Overrides returns whether type overrides other SdkMemberType
+	Overrides(SdkMemberType) bool
 }
 
 var _ sdkRegisterable = (SdkMemberType)(nil)
@@ -760,6 +770,13 @@
 type SdkMemberTypeBase struct {
 	PropertyName string
 
+	// Property names that this SdkMemberTypeBase can override, this is useful when a module type is a
+	// superset of another module type.
+	OverridesPropertyNames map[string]bool
+
+	// The names of linkage variants supported by this module.
+	SupportedLinkageNames []string
+
 	// When set to true BpPropertyNotRequired indicates that the member type does not require the
 	// property to be specifiable in an Android.bp file.
 	BpPropertyNotRequired bool
@@ -769,7 +786,12 @@
 	// If not specified then it is assumed to be available on all targeted build releases.
 	SupportedBuildReleaseSpecification string
 
-	SupportsSdk     bool
+	// Set to true if this must be usable with the sdk/sdk_snapshot module types. Otherwise, it will
+	// only be usable with module_exports/module_exports_snapshots module types.
+	SupportsSdk bool
+
+	// Set to true if prebuilt host artifacts of this member may be specific to the host OS. Only
+	// applicable to modules where HostSupported() is true.
 	HostOsDependent bool
 
 	// When set to true UseSourceModuleTypeInSnapshot indicates that the member type creates a source
@@ -777,6 +799,11 @@
 	// code from automatically adding a prefer: true flag.
 	UseSourceModuleTypeInSnapshot bool
 
+	// Set to proptools.BoolPtr(false) if this member does not generate prebuilts but is only provided
+	// to allow the sdk to gather members from this member's dependencies. If not specified then
+	// defaults to true.
+	PrebuiltsRequired *bool
+
 	// The list of supported traits.
 	Traits []SdkMemberTrait
 }
@@ -801,6 +828,10 @@
 	return b.HostOsDependent
 }
 
+func (b *SdkMemberTypeBase) ArePrebuiltsRequired() bool {
+	return proptools.BoolDefault(b.PrebuiltsRequired, true)
+}
+
 func (b *SdkMemberTypeBase) UsesSourceModuleTypeInSnapshot() bool {
 	return b.UseSourceModuleTypeInSnapshot
 }
@@ -809,6 +840,14 @@
 	return NewSdkMemberTraitSet(b.Traits)
 }
 
+func (b *SdkMemberTypeBase) Overrides(other SdkMemberType) bool {
+	return b.OverridesPropertyNames[other.SdkPropertyName()]
+}
+
+func (b *SdkMemberTypeBase) SupportedLinkages() []string {
+	return b.SupportedLinkageNames
+}
+
 // registeredModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports
 // modules.
 var registeredModuleExportsMemberTypes = &sdkRegistry{}
@@ -978,3 +1017,10 @@
 }
 
 var ExportedComponentsInfoProvider = blueprint.NewProvider(ExportedComponentsInfo{})
+
+// AdditionalSdkInfo contains additional properties to add to the generated SDK info file.
+type AdditionalSdkInfo struct {
+	Properties map[string]interface{}
+}
+
+var AdditionalSdkInfoProvider = blueprint.NewProvider(AdditionalSdkInfo{})
diff --git a/apex/Android.bp b/apex/Android.bp
index 41224ec..312aadb 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -22,8 +22,10 @@
     srcs: [
         "androidmk.go",
         "apex.go",
+        "apex_sdk_member.go",
         "apex_singleton.go",
         "builder.go",
+        "constants.go",
         "deapexer.go",
         "key.go",
         "prebuilt.go",
diff --git a/apex/apex.go b/apex/apex.go
index 951157f..744a9a6 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -604,32 +604,49 @@
 	// replacement. This is needed because some prebuilt modules do not provide all the information
 	// needed by the apex.
 	sourceOnly bool
+
+	// If not-nil and an APEX is a member of an SDK then dependencies of that APEX with this tag will
+	// also be added as exported members of that SDK.
+	memberType android.SdkMemberType
 }
 
-func (d dependencyTag) ReplaceSourceWithPrebuilt() bool {
+func (d *dependencyTag) SdkMemberType(_ android.Module) android.SdkMemberType {
+	return d.memberType
+}
+
+func (d *dependencyTag) ExportMember() bool {
+	return true
+}
+
+func (d *dependencyTag) String() string {
+	return fmt.Sprintf("apex.dependencyTag{%q}", d.name)
+}
+
+func (d *dependencyTag) ReplaceSourceWithPrebuilt() bool {
 	return !d.sourceOnly
 }
 
 var _ android.ReplaceSourceWithPrebuilt = &dependencyTag{}
+var _ android.SdkMemberDependencyTag = &dependencyTag{}
 
 var (
-	androidAppTag   = dependencyTag{name: "androidApp", payload: true}
-	bpfTag          = dependencyTag{name: "bpf", payload: true}
-	certificateTag  = dependencyTag{name: "certificate"}
-	executableTag   = dependencyTag{name: "executable", payload: true}
-	fsTag           = dependencyTag{name: "filesystem", payload: true}
-	bcpfTag         = dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true}
-	sscpfTag        = dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true}
-	compatConfigTag = dependencyTag{name: "compatConfig", payload: true, sourceOnly: true}
-	javaLibTag      = dependencyTag{name: "javaLib", payload: true}
-	jniLibTag       = dependencyTag{name: "jniLib", payload: true}
-	keyTag          = dependencyTag{name: "key"}
-	prebuiltTag     = dependencyTag{name: "prebuilt", payload: true}
-	rroTag          = dependencyTag{name: "rro", payload: true}
-	sharedLibTag    = dependencyTag{name: "sharedLib", payload: true}
-	testForTag      = dependencyTag{name: "test for"}
-	testTag         = dependencyTag{name: "test", payload: true}
-	shBinaryTag     = dependencyTag{name: "shBinary", payload: true}
+	androidAppTag   = &dependencyTag{name: "androidApp", payload: true}
+	bpfTag          = &dependencyTag{name: "bpf", payload: true}
+	certificateTag  = &dependencyTag{name: "certificate"}
+	executableTag   = &dependencyTag{name: "executable", payload: true}
+	fsTag           = &dependencyTag{name: "filesystem", payload: true}
+	bcpfTag         = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
+	sscpfTag        = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true, memberType: java.SystemServerClasspathFragmentSdkMemberType}
+	compatConfigTag = &dependencyTag{name: "compatConfig", payload: true, sourceOnly: true}
+	javaLibTag      = &dependencyTag{name: "javaLib", payload: true}
+	jniLibTag       = &dependencyTag{name: "jniLib", payload: true}
+	keyTag          = &dependencyTag{name: "key"}
+	prebuiltTag     = &dependencyTag{name: "prebuilt", payload: true}
+	rroTag          = &dependencyTag{name: "rro", payload: true}
+	sharedLibTag    = &dependencyTag{name: "sharedLib", payload: true}
+	testForTag      = &dependencyTag{name: "test for"}
+	testTag         = &dependencyTag{name: "test", payload: true}
+	shBinaryTag     = &dependencyTag{name: "shBinary", payload: true}
 )
 
 // TODO(jiyong): shorten this function signature
@@ -1735,7 +1752,7 @@
 		if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
 			return false
 		}
-		if dt, ok := depTag.(dependencyTag); ok && !dt.payload {
+		if dt, ok := depTag.(*dependencyTag); ok && !dt.payload {
 			return false
 		}
 
diff --git a/apex/apex_sdk_member.go b/apex/apex_sdk_member.go
new file mode 100644
index 0000000..284158f
--- /dev/null
+++ b/apex/apex_sdk_member.go
@@ -0,0 +1,58 @@
+// Copyright (C) 2022 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 apex
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+// This file contains support for using apex modules within an sdk.
+
+func init() {
+	// Register sdk member types.
+	android.RegisterSdkMemberType(&apexSdkMemberType{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
+			PropertyName: "apexes",
+			SupportsSdk:  true,
+
+			// The apexes property does not need to be included in the snapshot as adding an apex to an
+			// sdk does not produce any prebuilts of the apex.
+			PrebuiltsRequired: proptools.BoolPtr(false),
+		},
+	})
+}
+
+type apexSdkMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (mt *apexSdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (mt *apexSdkMemberType) IsInstance(module android.Module) bool {
+	_, ok := module.(*apexBundle)
+	return ok
+}
+
+func (mt *apexSdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
+	panic("Sdk does not create prebuilts of the apexes in its snapshot")
+}
+
+func (mt *apexSdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+	panic("Sdk does not create prebuilts of the apexes in its snapshot")
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index b3036b1..dbe9180 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -5931,7 +5931,7 @@
 func TestApexAvailable_IndirectDep(t *testing.T) {
 	// libbbaz is an indirect dep
 	testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'.\n\nDependency path:
-.*via tag apex\.dependencyTag.*name:sharedLib.*
+.*via tag apex\.dependencyTag\{"sharedLib"\}
 .*-> libfoo.*link:shared.*
 .*via tag cc\.libraryDependencyTag.*Kind:sharedLibraryDependency.*
 .*-> libbar.*link:shared.*
@@ -6227,6 +6227,9 @@
 			name: "mybootclasspath_fragment",
 			contents: ["bcplib"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -6241,6 +6244,9 @@
 			name: "override_bootclasspath_fragment",
 			contents: ["override_bcplib"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -7294,6 +7300,9 @@
 			apex_available: [
 				"some-non-updatable-apex",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -7352,6 +7361,9 @@
 			apex_available: [
 				"com.android.art.debug",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		apex_key {
@@ -8796,6 +8808,9 @@
 			name: "mybootclasspathfragment",
 			contents: ["mybootclasspathlib"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -9116,6 +9131,9 @@
 				name: "mybootclasspathfragment",
 				contents: ["mybootclasspathlib"],
 				apex_available: ["myapex"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_sdk_library {
@@ -9216,6 +9234,9 @@
 					name: "mybootclasspathfragment",
 					contents: ["mybootclasspathlib"],
 					apex_available: ["myapex"],
+					hidden_api: {
+						split_packages: ["*"],
+					},
 				}
 
 				java_sdk_library {
@@ -9435,6 +9456,9 @@
 			name: "mybootclasspathfragment",
 			contents: ["myjavalib"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 		java_library {
 			name: "myjavalib",
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index ce6b7f7..b298dac 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -110,6 +110,9 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 `,
 	)
@@ -209,6 +212,9 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		bootclasspath_fragment {
@@ -220,6 +226,9 @@
 							module: "art-bootclasspath-fragment",
 					},
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 `,
 	)
@@ -361,6 +370,9 @@
 				apex_available: [
 					"com.android.art",
 				],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 		`, contentsInsert(contents))
 
@@ -853,6 +865,9 @@
 			apex_available: [
 				"myapex",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 	`)
 
@@ -959,6 +974,9 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		apex {
@@ -1010,6 +1028,9 @@
 					module: "art-bootclasspath-fragment",
 				},
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 	`)
 
@@ -1123,6 +1144,9 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		apex {
@@ -1175,6 +1199,9 @@
 					module: "art-bootclasspath-fragment",
 				},
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 	`)
 
@@ -1282,6 +1309,9 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		apex {
@@ -1334,6 +1364,9 @@
 					module: "art-bootclasspath-fragment",
 				},
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 	`)
 
diff --git a/apex/builder.go b/apex/builder.go
index a21fcb8..1956b44 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -76,11 +76,12 @@
 		Command: `rm -f $out && ${jsonmodify} $in ` +
 			`-a provideNativeLibs ${provideNativeLibs} ` +
 			`-a requireNativeLibs ${requireNativeLibs} ` +
+			`-se version 0 ${default_version} ` +
 			`${opt} ` +
 			`-o $out`,
 		CommandDeps: []string{"${jsonmodify}"},
 		Description: "prepare ${out}",
-	}, "provideNativeLibs", "requireNativeLibs", "opt")
+	}, "provideNativeLibs", "requireNativeLibs", "default_version", "opt")
 
 	stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{
 		Command:     `rm -f $out && ${conv_apex_manifest} strip $in -o $out`,
@@ -213,6 +214,7 @@
 		Args: map[string]string{
 			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
 			"requireNativeLibs": strings.Join(requireNativeLibs, " "),
+			"default_version":   defaultManifestVersion,
 			"opt":               strings.Join(optCommands, " "),
 		},
 	})
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
index 60f18bd..9142eed 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -88,6 +88,9 @@
 				"baz",
 				"quuz",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -134,6 +137,9 @@
 			contents: [
 				"bar",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
diff --git a/apex/constants.go b/apex/constants.go
new file mode 100644
index 0000000..c68edb7
--- /dev/null
+++ b/apex/constants.go
@@ -0,0 +1,36 @@
+// Copyright (C) 2022 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 apex
+
+// This file contains branch specific constants. They are stored in a separate
+// file to minimise the potential of merge conflicts between branches when
+// the code from the package is changed.
+
+// The default manifest version for all the modules on this branch.
+// This version code will be used only if there is no version field in the
+// module's apex_manifest.json. Release branches have their version injected
+// into apex_manifest.json by the tooling and will not use the version set
+// here. Developers can also set the version field locally in the
+// apex_manifest.json to build a module with a specific version.
+//
+// The value follows the schema from go/mainline-version-codes, and is chosen
+// based on the branch such that the builds from testing and development
+// branches will have a version higher than the prebuilts.
+// Versions per branch:
+// * x-dev           - xx0090000 (where xx is the branch SDK level)
+// * AOSP            - xx9990000
+// * x-mainline-prod - xx9990000
+// * master          - 990090000
+const defaultManifestVersion = "339990000"
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 06c39ee..4b48da8 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -125,6 +125,7 @@
 					unsupported_packages: [
 							"bar-unsupported-packages.txt",
 					],
+					split_packages: ["*"],
 				},
 			}
 
@@ -274,6 +275,9 @@
 				"baz",
 				"quuz",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -317,6 +321,9 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["bar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		apex_key {
@@ -482,6 +489,9 @@
 			contents: [
 				"foo", "bar",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		prebuilt_bootclasspath_fragment {
@@ -599,6 +609,9 @@
 				generate_classpaths_proto: false,
 				contents: ["foo"],
 				apex_available: ["myapex"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_library {
@@ -656,6 +669,9 @@
 				contents: [
 					"foo",
 				],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			platform_bootclasspath {
@@ -696,6 +712,9 @@
 			bootclasspath_fragment {
 				name: "not-in-apex-fragment",
 				contents: ["foo"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			platform_bootclasspath {
@@ -746,6 +765,9 @@
 				name: "apex-fragment",
 				contents: ["foo", "bar"],
 				apex_available:[ "myapex" ],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			platform_bootclasspath {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 09cc352..893c52e 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -4076,7 +4076,7 @@
 		{
 			name:     "assemble",
 			src:      "foo.s",
-			expected: combineSlices(baseExpectedFlags, []string{"-D__ASSEMBLY__"}, expectedIncludes, lastIncludes),
+			expected: combineSlices(baseExpectedFlags, []string{"-D__ASSEMBLY__", "-fdebug-default-version=4"}, expectedIncludes, lastIncludes),
 		},
 	}
 
diff --git a/cc/compiler.go b/cc/compiler.go
index eb5458f..383eed0 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -491,6 +491,10 @@
 
 	flags.Global.AsFlags = append(flags.Global.AsFlags, "-D__ASSEMBLY__")
 
+	// TODO(b/235105792): override global -fdebug-default-version=5, it is causing $TMPDIR to
+	// end up in the dwarf data for crtend_so.S.
+	flags.Global.AsFlags = append(flags.Global.AsFlags, "-fdebug-default-version=4")
+
 	flags.Global.CppFlags = append(flags.Global.CppFlags, tc.Cppflags())
 
 	flags.Global.YasmFlags = append(flags.Global.YasmFlags, tc.YasmFlags())
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 8988de2..1bcbdc5 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -27,32 +27,33 @@
 
 var sharedLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
-		PropertyName:    "native_shared_libs",
-		SupportsSdk:     true,
-		HostOsDependent: true,
+		PropertyName:          "native_shared_libs",
+		SupportsSdk:           true,
+		HostOsDependent:       true,
+		SupportedLinkageNames: []string{"shared"},
 	},
 	prebuiltModuleType: "cc_prebuilt_library_shared",
-	linkTypes:          []string{"shared"},
 }
 
 var staticLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
-		PropertyName:    "native_static_libs",
-		SupportsSdk:     true,
-		HostOsDependent: true,
+		PropertyName:          "native_static_libs",
+		SupportsSdk:           true,
+		HostOsDependent:       true,
+		SupportedLinkageNames: []string{"static"},
 	},
 	prebuiltModuleType: "cc_prebuilt_library_static",
-	linkTypes:          []string{"static"},
 }
 
 var staticAndSharedLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
-		PropertyName:    "native_libs",
-		SupportsSdk:     true,
-		HostOsDependent: true,
+		PropertyName:           "native_libs",
+		OverridesPropertyNames: map[string]bool{"native_shared_libs": true, "native_static_libs": true},
+		SupportsSdk:            true,
+		HostOsDependent:        true,
+		SupportedLinkageNames:  []string{"static", "shared"},
 	},
 	prebuiltModuleType: "cc_prebuilt_library",
-	linkTypes:          []string{"static", "shared"},
 }
 
 func init() {
@@ -69,9 +70,6 @@
 
 	noOutputFiles bool // True if there are no srcs files.
 
-	// The set of link types supported. A set of "static", "shared", or nil to
-	// skip link type variations.
-	linkTypes []string
 }
 
 func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
@@ -165,12 +163,12 @@
 				// Add any additional dependencies needed.
 				variations = append(variations, dependency.imageVariations...)
 
-				if mt.linkTypes == nil {
+				if mt.SupportedLinkageNames == nil {
 					// No link types are supported so add a dependency directly.
 					ctx.AddFarVariationDependencies(variations, dependencyTag, name)
 				} else {
 					// Otherwise, add a dependency on each supported link type in turn.
-					for _, linkType := range mt.linkTypes {
+					for _, linkType := range mt.SupportedLinkageNames {
 						libVariations := append(variations,
 							blueprint.Variation{Mutator: "link", Variation: linkType})
 						// If this is for the device and a shared link type then add a dependency onto the
diff --git a/cc/object.go b/cc/object.go
index bd5bd45..3558efb 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -37,7 +37,6 @@
 		SupportsSdk:  true,
 	},
 	prebuiltModuleType: "cc_prebuilt_object",
-	linkTypes:          nil,
 }
 
 type objectLinker struct {
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 4b3161b..3f8105c 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -183,8 +183,7 @@
 	touch(shared.JoinPath(topDir, queryviewMarker))
 }
 
-func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler) {
-	metricsDir := configuration.Getenv("LOG_DIR")
+func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler, metricsDir string) {
 	if len(metricsDir) < 1 {
 		fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n")
 		os.Exit(1)
@@ -238,7 +237,7 @@
 // doChosenActivity runs Soong for a specific activity, like bp2build, queryview
 // or the actual Soong build for the build.ninja file. Returns the top level
 // output file of the specific activity.
-func doChosenActivity(configuration android.Config, extraNinjaDeps []string) string {
+func doChosenActivity(configuration android.Config, extraNinjaDeps []string, logDir string) string {
 	mixedModeBuild := configuration.BazelContext.BazelEnabled()
 	generateBazelWorkspace := bp2buildMarker != ""
 	generateQueryView := bazelQueryViewDir != ""
@@ -302,7 +301,7 @@
 		}
 	}
 
-	writeMetrics(configuration, *ctx.EventHandler)
+	writeMetrics(configuration, *ctx.EventHandler, logDir)
 	return cmdlineArgs.OutFile
 }
 
@@ -358,7 +357,11 @@
 		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve"))
 	}
 
-	finalOutputFile := doChosenActivity(configuration, extraNinjaDeps)
+	// Bypass configuration.Getenv, as LOG_DIR does not need to be dependency tracked. By definition, it will
+	// change between every CI build, so tracking it would require re-running Soong for every build.
+	logDir := availableEnv["LOG_DIR"]
+
+	finalOutputFile := doChosenActivity(configuration, extraNinjaDeps, logDir)
 
 	writeUsedEnvironmentFile(configuration, finalOutputFile)
 }
diff --git a/java/base.go b/java/base.go
index 7aa2814..717c727 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1419,17 +1419,18 @@
 	j.implementationAndResourcesJar = implementationAndResourcesJar
 
 	// Enable dex compilation for the APEX variants, unless it is disabled explicitly
+	compileDex := j.dexProperties.Compile_dex
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if j.DirectlyInAnyApex() && !apexInfo.IsForPlatform() {
-		if j.dexProperties.Compile_dex == nil {
-			j.dexProperties.Compile_dex = proptools.BoolPtr(true)
+		if compileDex == nil {
+			compileDex = proptools.BoolPtr(true)
 		}
 		if j.deviceProperties.Hostdex == nil {
 			j.deviceProperties.Hostdex = proptools.BoolPtr(true)
 		}
 	}
 
-	if ctx.Device() && (Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) {
+	if ctx.Device() && (Bool(j.properties.Installable) || Bool(compileDex)) {
 		if j.hasCode(ctx) {
 			if j.shouldInstrumentStatic(ctx) {
 				j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles,
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 9600935..b6b179c 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -32,19 +32,24 @@
 func init() {
 	registerBootclasspathFragmentBuildComponents(android.InitRegistrationContext)
 
-	android.RegisterSdkMemberType(&bootclasspathFragmentMemberType{
-		SdkMemberTypeBase: android.SdkMemberTypeBase{
-			PropertyName: "bootclasspath_fragments",
-			SupportsSdk:  true,
-		},
-	})
+	android.RegisterSdkMemberType(BootclasspathFragmentSdkMemberType)
 }
 
 func registerBootclasspathFragmentBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("bootclasspath_fragment", bootclasspathFragmentFactory)
+	ctx.RegisterModuleType("bootclasspath_fragment_test", testBootclasspathFragmentFactory)
 	ctx.RegisterModuleType("prebuilt_bootclasspath_fragment", prebuiltBootclasspathFragmentFactory)
 }
 
+// BootclasspathFragmentSdkMemberType is the member type used to add bootclasspath_fragments to
+// the SDK snapshot. It is exported for use by apex.
+var BootclasspathFragmentSdkMemberType = &bootclasspathFragmentMemberType{
+	SdkMemberTypeBase: android.SdkMemberTypeBase{
+		PropertyName: "bootclasspath_fragments",
+		SupportsSdk:  true,
+	},
+}
+
 type bootclasspathFragmentContentDependencyTag struct {
 	blueprint.BaseDependencyTag
 }
@@ -227,6 +232,9 @@
 	android.SdkBase
 	ClasspathFragmentBase
 
+	// True if this fragment is for testing purposes.
+	testFragment bool
+
 	properties bootclasspathFragmentProperties
 
 	sourceOnlyProperties SourceOnlyBootclasspathProperties
@@ -298,6 +306,12 @@
 	return m
 }
 
+func testBootclasspathFragmentFactory() android.Module {
+	m := bootclasspathFragmentFactory().(*BootclasspathFragmentModule)
+	m.testFragment = true
+	return m
+}
+
 // bootclasspathFragmentInitContentsFromImage will initialize the contents property from the image_name if
 // necessary.
 func bootclasspathFragmentInitContentsFromImage(ctx android.EarlyModuleContext, m *BootclasspathFragmentModule) {
@@ -819,6 +833,26 @@
 	return input
 }
 
+// isTestFragment returns true if the current module is a test bootclasspath_fragment.
+func (b *BootclasspathFragmentModule) isTestFragment() bool {
+	if b.testFragment {
+		return true
+	}
+
+	// TODO(b/194063708): Once test fragments all use bootclasspath_fragment_test
+	// Some temporary exceptions until all test fragments use the
+	// bootclasspath_fragment_test module type.
+	name := b.BaseModuleName()
+	if strings.HasPrefix(name, "test_") {
+		return true
+	}
+	if name == "apex.apexd_test_bootclasspath-fragment" {
+		return true
+	}
+
+	return false
+}
+
 // produceHiddenAPIOutput produces the hidden API all-flags.csv file (and supporting files)
 // for the fragment as well as encoding the flags in the boot dex jars.
 func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
@@ -832,11 +866,18 @@
 	packagePrefixes := b.sourceOnlyProperties.Hidden_api.Package_prefixes
 	singlePackages := b.sourceOnlyProperties.Hidden_api.Single_packages
 	if splitPackages != nil || packagePrefixes != nil || singlePackages != nil {
-		if splitPackages == nil {
-			splitPackages = []string{"*"}
-		}
 		output.SignaturePatternsPath = buildRuleSignaturePatternsFile(
 			ctx, output.AllFlagsPath, splitPackages, packagePrefixes, singlePackages)
+	} else if !b.isTestFragment() {
+		ctx.ModuleErrorf(`Must specify at least one of the split_packages, package_prefixes and single_packages properties
+  If this is a new bootclasspath_fragment or you are unsure what to do add the
+  the following to the bootclasspath_fragment:
+      hidden_api: {split_packages: ["*"]},
+  and then run the following:
+      m analyze_bcpf && analyze_bcpf --bcpf %q
+  it will analyze the bootclasspath_fragment and provide hints as to what you
+  should specify here. If you are happy with its suggestions then you can add
+  the --fix option and it will fix them for you.`, b.BaseModuleName())
 	}
 
 	return output
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index d3de675..83beb6d 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -121,6 +121,9 @@
 					],
 				},
 			},
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -201,6 +204,9 @@
 			core_platform_api: {
 				stub_libs: ["mycoreplatform.stubs"],
 			},
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -278,3 +284,64 @@
 
 	android.AssertPathsRelativeToTopEquals(t, "widest dex stubs jar", expectedWidestPaths, info.TransitiveStubDexJarsByScope.StubDexJarsForWidestAPIScope())
 }
+
+func TestBootclasspathFragment_Test(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootclasspathFragment,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("mysdklibrary"),
+	).RunTestWithBp(t, `
+		bootclasspath_fragment {
+			name: "myfragment",
+			contents: ["mysdklibrary"],
+			hidden_api: {
+				split_packages: [],
+			},
+		}
+
+		bootclasspath_fragment {
+			name: "test_fragment",
+			contents: ["mysdklibrary"],
+			hidden_api: {
+				split_packages: [],
+			},
+		}
+
+		bootclasspath_fragment {
+			name: "apex.apexd_test_bootclasspath-fragment",
+			contents: ["mysdklibrary"],
+			hidden_api: {
+				split_packages: [],
+			},
+		}
+
+		bootclasspath_fragment_test {
+			name: "a_test_fragment",
+			contents: ["mysdklibrary"],
+			hidden_api: {
+				split_packages: [],
+			},
+		}
+
+
+		java_sdk_library {
+			name: "mysdklibrary",
+			srcs: ["a.java"],
+			shared_library: false,
+			public: {enabled: true},
+			system: {enabled: true},
+		}
+	`)
+
+	fragment := result.Module("myfragment", "android_common").(*BootclasspathFragmentModule)
+	android.AssertBoolEquals(t, "not a test fragment", false, fragment.isTestFragment())
+
+	fragment = result.Module("test_fragment", "android_common").(*BootclasspathFragmentModule)
+	android.AssertBoolEquals(t, "is a test fragment by prefix", true, fragment.isTestFragment())
+
+	fragment = result.Module("a_test_fragment", "android_common").(*BootclasspathFragmentModule)
+	android.AssertBoolEquals(t, "is a test fragment by type", true, fragment.isTestFragment())
+
+	fragment = result.Module("apex.apexd_test_bootclasspath-fragment", "android_common").(*BootclasspathFragmentModule)
+	android.AssertBoolEquals(t, "is a test fragment by name", true, fragment.isTestFragment())
+}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 3b1f7c0..20ea5ff 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -675,87 +675,16 @@
 
 	zipSyncCleanupCmd(rule, srcJarDir)
 
-	rule.Build("metalava", "metalava merged")
-
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
+		d.generateCheckCurrentCheckedInApiIsUpToDateBuildRules(ctx)
 
-		if len(d.Javadoc.properties.Out) > 0 {
-			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
-		}
-
-		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
-		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
-		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
-
-		if baselineFile.Valid() {
-			ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
-		}
-
-		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
-
-		rule := android.NewRuleBuilder(pctx, ctx)
-
-		// Diff command line.
-		// -F matches the closest "opening" line, such as "package android {"
-		// and "  public class Intent {".
-		diff := `diff -u -F '{ *$'`
-
-		rule.Command().Text("( true")
-		rule.Command().
-			Text(diff).
-			Input(apiFile).Input(d.apiFile)
-
-		rule.Command().
-			Text(diff).
-			Input(removedApiFile).Input(d.removedApiFile)
-
-		msg := fmt.Sprintf(`\n******************************\n`+
-			`You have tried to change the API from what has been previously approved.\n\n`+
-			`To make these errors go away, you have two choices:\n`+
-			`   1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
-			`      to the new methods, etc. shown in the above diff.\n\n`+
-			`   2. You can update current.txt and/or removed.txt by executing the following command:\n`+
-			`         m %s-update-current-api\n\n`+
-			`      To submit the revised current.txt to the main Android repository,\n`+
-			`      you will need approval.\n`+
-			`******************************\n`, ctx.ModuleName())
-
-		rule.Command().
-			Text("touch").Output(d.checkCurrentApiTimestamp).
-			Text(") || (").
-			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
-			Text("; exit 38").
-			Text(")")
-
-		rule.Build("metalavaCurrentApiCheck", "check current API")
-
-		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
-
-		// update API rule
-		rule = android.NewRuleBuilder(pctx, ctx)
-
-		rule.Command().Text("( true")
-
-		rule.Command().
-			Text("cp").Flag("-f").
-			Input(d.apiFile).Flag(apiFile.String())
-
-		rule.Command().
-			Text("cp").Flag("-f").
-			Input(d.removedApiFile).Flag(removedApiFile.String())
-
-		msg = "failed to update public API"
-
-		rule.Command().
-			Text("touch").Output(d.updateCurrentApiTimestamp).
-			Text(") || (").
-			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
-			Text("; exit 38").
-			Text(")")
-
-		rule.Build("metalavaCurrentApiUpdate", "update current API")
+		// Make sure that whenever the API stubs are generated that the current checked in API files are
+		// checked to make sure that they are up-to-date.
+		cmd.Validation(d.checkCurrentApiTimestamp)
 	}
 
+	rule.Build("metalava", "metalava merged")
+
 	if String(d.properties.Check_nullability_warnings) != "" {
 		if d.nullabilityWarningsFile == nil {
 			ctx.PropertyErrorf("check_nullability_warnings",
@@ -792,6 +721,84 @@
 	}
 }
 
+func (d *Droidstubs) generateCheckCurrentCheckedInApiIsUpToDateBuildRules(ctx android.ModuleContext) {
+	if len(d.Javadoc.properties.Out) > 0 {
+		ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+	}
+
+	apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
+	removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
+	baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
+
+	if baselineFile.Valid() {
+		ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
+	}
+
+	d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+
+	// Diff command line.
+	// -F matches the closest "opening" line, such as "package android {"
+	// and "  public class Intent {".
+	diff := `diff -u -F '{ *$'`
+
+	rule.Command().Text("( true")
+	rule.Command().
+		Text(diff).
+		Input(apiFile).Input(d.apiFile)
+
+	rule.Command().
+		Text(diff).
+		Input(removedApiFile).Input(d.removedApiFile)
+
+	msg := fmt.Sprintf(`\n******************************\n`+
+		`You have tried to change the API from what has been previously approved.\n\n`+
+		`To make these errors go away, you have two choices:\n`+
+		`   1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
+		`      to the new methods, etc. shown in the above diff.\n\n`+
+		`   2. You can update current.txt and/or removed.txt by executing the following command:\n`+
+		`         m %s-update-current-api\n\n`+
+		`      To submit the revised current.txt to the main Android repository,\n`+
+		`      you will need approval.\n`+
+		`******************************\n`, ctx.ModuleName())
+
+	rule.Command().
+		Text("touch").Output(d.checkCurrentApiTimestamp).
+		Text(") || (").
+		Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+		Text("; exit 38").
+		Text(")")
+
+	rule.Build("metalavaCurrentApiCheck", "check current API")
+
+	d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
+
+	// update API rule
+	rule = android.NewRuleBuilder(pctx, ctx)
+
+	rule.Command().Text("( true")
+
+	rule.Command().
+		Text("cp").Flag("-f").
+		Input(d.apiFile).Flag(apiFile.String())
+
+	rule.Command().
+		Text("cp").Flag("-f").
+		Input(d.removedApiFile).Flag(removedApiFile.String())
+
+	msg = "failed to update public API"
+
+	rule.Command().
+		Text("touch").Output(d.updateCurrentApiTimestamp).
+		Text(") || (").
+		Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+		Text("; exit 38").
+		Text(")")
+
+	rule.Build("metalavaCurrentApiUpdate", "update current API")
+}
+
 func StubsDefaultsFactory() android.Module {
 	module := &DocDefaults{}
 
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 44650a6..9449707 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -212,6 +212,10 @@
 	mctx.CreateModule(systemModulesImportFactory, &props)
 }
 
+func PrebuiltApiModuleName(module, scope, version string) string {
+	return module + ".api." + scope + "." + version
+}
+
 func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
 	// <apiver>/<scope>/api/<module>.txt
 	apiLevelFiles := globApiDirs(mctx, p, "api/*.txt")
@@ -220,12 +224,9 @@
 	}
 
 	// Create modules for all (<module>, <scope, <version>) triplets,
-	apiModuleName := func(module, scope, version string) string {
-		return module + ".api." + scope + "." + version
-	}
 	for _, f := range apiLevelFiles {
 		module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
-		createApiModule(mctx, apiModuleName(module, scope, strconv.Itoa(version)), f)
+		createApiModule(mctx, PrebuiltApiModuleName(module, scope, strconv.Itoa(version)), f)
 	}
 
 	// Figure out the latest version of each module/scope
@@ -266,7 +267,7 @@
 	// Sort the keys in order to make build.ninja stable
 	for _, k := range android.SortedStringKeys(latest) {
 		info := latest[k]
-		name := apiModuleName(info.module, info.scope, "latest")
+		name := PrebuiltApiModuleName(info.module, info.scope, "latest")
 		createApiModule(mctx, name, info.path)
 	}
 
@@ -278,7 +279,7 @@
 			filename, _, scope := parsePrebuiltPath(mctx, f)
 			referencedModule := strings.TrimSuffix(filename, "-incompatibilities")
 
-			createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), f)
+			createApiModule(mctx, PrebuiltApiModuleName(referencedModule+"-incompatibilities", scope, "latest"), f)
 
 			incompatibilities[referencedModule+"."+scope] = true
 		}
@@ -286,7 +287,7 @@
 	// Create empty incompatibilities files for remaining modules
 	for _, k := range android.SortedStringKeys(latest) {
 		if _, ok := incompatibilities[k]; !ok {
-			createEmptyFile(mctx, apiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest"))
+			createEmptyFile(mctx, PrebuiltApiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest"))
 		}
 	}
 }
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 47ffc6a..591e90f 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -97,6 +97,13 @@
 	// The tag to use to depend on the stubs source and API module.
 	stubsSourceAndApiTag scopeDependencyTag
 
+	// The tag to use to depend on the module that provides the latest version of the API .txt file.
+	latestApiModuleTag scopeDependencyTag
+
+	// The tag to use to depend on the module that provides the latest version of the API removed.txt
+	// file.
+	latestRemovedApiModuleTag scopeDependencyTag
+
 	// The scope specific prefix to add to the api file base of "current.txt" or "removed.txt".
 	apiFilePrefix string
 
@@ -158,6 +165,16 @@
 		apiScope:         scope,
 		depInfoExtractor: (*scopePaths).extractStubsSourceAndApiInfoFromApiStubsProvider,
 	}
+	scope.latestApiModuleTag = scopeDependencyTag{
+		name:             name + "-latest-api",
+		apiScope:         scope,
+		depInfoExtractor: (*scopePaths).extractLatestApiPath,
+	}
+	scope.latestRemovedApiModuleTag = scopeDependencyTag{
+		name:             name + "-latest-removed-api",
+		apiScope:         scope,
+		depInfoExtractor: (*scopePaths).extractLatestRemovedApiPath,
+	}
 
 	// To get the args needed to generate the stubs source append all the args from
 	// this scope and all the scopes it extends as each set of args adds additional
@@ -203,6 +220,24 @@
 	return scope.name
 }
 
+// snapshotRelativeDir returns the snapshot directory into which the files related to scopes will
+// be stored.
+func (scope *apiScope) snapshotRelativeDir() string {
+	return filepath.Join("sdk_library", scope.name)
+}
+
+// snapshotRelativeCurrentApiTxtPath returns the snapshot path to the API .txt file for the named
+// library.
+func (scope *apiScope) snapshotRelativeCurrentApiTxtPath(name string) string {
+	return filepath.Join(scope.snapshotRelativeDir(), name+".txt")
+}
+
+// snapshotRelativeRemovedApiTxtPath returns the snapshot path to the removed API .txt file for the
+// named library.
+func (scope *apiScope) snapshotRelativeRemovedApiTxtPath(name string) string {
+	return filepath.Join(scope.snapshotRelativeDir(), name+"-removed.txt")
+}
+
 type apiScopes []*apiScope
 
 func (scopes apiScopes) Strings(accessor func(*apiScope) string) []string {
@@ -539,6 +574,12 @@
 
 	// Extracted annotations.
 	annotationsZip android.OptionalPath
+
+	// The path to the latest API file.
+	latestApiPath android.OptionalPath
+
+	// The path to the latest removed API file.
+	latestRemovedApiPath android.OptionalPath
 }
 
 func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error {
@@ -602,6 +643,31 @@
 	})
 }
 
+func extractSingleOptionalOutputPath(dep android.Module) (android.OptionalPath, error) {
+	var paths android.Paths
+	if sourceFileProducer, ok := dep.(android.SourceFileProducer); ok {
+		paths = sourceFileProducer.Srcs()
+	} else {
+		return android.OptionalPath{}, fmt.Errorf("module %q does not produce source files", dep)
+	}
+	if len(paths) != 1 {
+		return android.OptionalPath{}, fmt.Errorf("expected one path from %q, got %q", dep, paths)
+	}
+	return android.OptionalPathForPath(paths[0]), nil
+}
+
+func (paths *scopePaths) extractLatestApiPath(ctx android.ModuleContext, dep android.Module) error {
+	outputPath, err := extractSingleOptionalOutputPath(dep)
+	paths.latestApiPath = outputPath
+	return err
+}
+
+func (paths *scopePaths) extractLatestRemovedApiPath(ctx android.ModuleContext, dep android.Module) error {
+	outputPath, err := extractSingleOptionalOutputPath(dep)
+	paths.latestRemovedApiPath = outputPath
+	return err
+}
+
 type commonToSdkLibraryAndImportProperties struct {
 	// The naming scheme to use for the components that this module creates.
 	//
@@ -1174,6 +1240,16 @@
 
 		// Add a dependency on the stubs source in order to access both stubs source and api information.
 		ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceModuleName(apiScope))
+
+		if module.compareAgainstLatestApi(apiScope) {
+			// Add dependencies on the latest finalized version of the API .txt file.
+			latestApiModuleName := module.latestApiModuleName(apiScope)
+			ctx.AddDependency(module, apiScope.latestApiModuleTag, latestApiModuleName)
+
+			// Add dependencies on the latest finalized version of the remove API .txt file.
+			latestRemovedApiModuleName := module.latestRemovedApiModuleName(apiScope)
+			ctx.AddDependency(module, apiScope.latestRemovedApiModuleTag, latestRemovedApiModuleName)
+		}
 	}
 
 	if module.requiresRuntimeImplementationLibrary() {
@@ -1194,13 +1270,13 @@
 		if apiScope.unstable {
 			continue
 		}
-		if m := android.SrcIsModule(module.latestApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+		if m := module.latestApiModuleName(apiScope); !ctx.OtherModuleExists(m) {
 			missingApiModules = append(missingApiModules, m)
 		}
-		if m := android.SrcIsModule(module.latestRemovedApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+		if m := module.latestRemovedApiModuleName(apiScope); !ctx.OtherModuleExists(m) {
 			missingApiModules = append(missingApiModules, m)
 		}
-		if m := android.SrcIsModule(module.latestIncompatibilitiesFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+		if m := module.latestIncompatibilitiesModuleName(apiScope); !ctx.OtherModuleExists(m) {
 			missingApiModules = append(missingApiModules, m)
 		}
 	}
@@ -1274,6 +1350,26 @@
 	// Make the set of components exported by this module available for use elsewhere.
 	exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedStringKeys(exportedComponents)}
 	ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo)
+
+	// Provide additional information for inclusion in an sdk's generated .info file.
+	additionalSdkInfo := map[string]interface{}{}
+	additionalSdkInfo["dist_stem"] = module.distStem()
+	baseModuleName := module.BaseModuleName()
+	scopes := map[string]interface{}{}
+	additionalSdkInfo["scopes"] = scopes
+	for scope, scopePaths := range module.scopePaths {
+		scopeInfo := map[string]interface{}{}
+		scopes[scope.name] = scopeInfo
+		scopeInfo["current_api"] = scope.snapshotRelativeCurrentApiTxtPath(baseModuleName)
+		scopeInfo["removed_api"] = scope.snapshotRelativeRemovedApiTxtPath(baseModuleName)
+		if p := scopePaths.latestApiPath; p.Valid() {
+			scopeInfo["latest_api"] = p.Path().String()
+		}
+		if p := scopePaths.latestRemovedApiPath; p.Valid() {
+			scopeInfo["latest_removed_api"] = p.Path().String()
+		}
+	}
+	ctx.SetProvider(android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo})
 }
 
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
@@ -1319,16 +1415,32 @@
 	return proptools.StringDefault(module.sdkLibraryProperties.Dist_group, "unknown")
 }
 
+func latestPrebuiltApiModuleName(name string, apiScope *apiScope) string {
+	return PrebuiltApiModuleName(name, apiScope.name, "latest")
+}
+
 func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string {
-	return ":" + module.distStem() + ".api." + apiScope.name + ".latest"
+	return ":" + module.latestApiModuleName(apiScope)
+}
+
+func (module *SdkLibrary) latestApiModuleName(apiScope *apiScope) string {
+	return latestPrebuiltApiModuleName(module.distStem(), apiScope)
 }
 
 func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) string {
-	return ":" + module.distStem() + "-removed.api." + apiScope.name + ".latest"
+	return ":" + module.latestRemovedApiModuleName(apiScope)
+}
+
+func (module *SdkLibrary) latestRemovedApiModuleName(apiScope *apiScope) string {
+	return latestPrebuiltApiModuleName(module.distStem()+"-removed", apiScope)
 }
 
 func (module *SdkLibrary) latestIncompatibilitiesFilegroupName(apiScope *apiScope) string {
-	return ":" + module.distStem() + "-incompatibilities.api." + apiScope.name + ".latest"
+	return ":" + module.latestIncompatibilitiesModuleName(apiScope)
+}
+
+func (module *SdkLibrary) latestIncompatibilitiesModuleName(apiScope *apiScope) string {
+	return latestPrebuiltApiModuleName(module.distStem()+"-incompatibilities", apiScope)
 }
 
 func childModuleVisibility(childVisibility []string) []string {
@@ -1557,7 +1669,7 @@
 	props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
 	props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
 
-	if !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api) {
+	if module.compareAgainstLatestApi(apiScope) {
 		// check against the latest released API
 		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
 		props.Previous_api = latestApiFilegroupName
@@ -1609,6 +1721,10 @@
 	mctx.CreateModule(DroidstubsFactory, &props)
 }
 
+func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool {
+	return !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api)
+}
+
 // Implements android.ApexModule
 func (module *SdkLibrary) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool {
 	depTag := mctx.OtherModuleDependencyTag(dep)
@@ -2906,7 +3022,7 @@
 		if properties, ok := s.Scopes[apiScope]; ok {
 			scopeSet := propertySet.AddPropertySet(apiScope.propertyName)
 
-			scopeDir := filepath.Join("sdk_library", s.OsPrefix(), apiScope.name)
+			scopeDir := apiScope.snapshotRelativeDir()
 
 			var jars []string
 			for _, p := range properties.Jars {
@@ -2930,13 +3046,13 @@
 			}
 
 			if properties.CurrentApiFile != nil {
-				currentApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+".txt")
+				currentApiSnapshotPath := apiScope.snapshotRelativeCurrentApiTxtPath(ctx.Name())
 				ctx.SnapshotBuilder().CopyToSnapshot(properties.CurrentApiFile, currentApiSnapshotPath)
 				scopeSet.AddProperty("current_api", currentApiSnapshotPath)
 			}
 
 			if properties.RemovedApiFile != nil {
-				removedApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+"-removed.txt")
+				removedApiSnapshotPath := apiScope.snapshotRelativeRemovedApiTxtPath(ctx.Name())
 				ctx.SnapshotBuilder().CopyToSnapshot(properties.RemovedApiFile, removedApiSnapshotPath)
 				scopeSet.AddProperty("removed_api", removedApiSnapshotPath)
 			}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 3500c84..805bc22 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -126,6 +126,10 @@
 
 	exportedComponentsInfo := result.ModuleProvider(foo.Module(), android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
 	expectedFooExportedComponents := []string{
+		"foo-removed.api.public.latest",
+		"foo-removed.api.system.latest",
+		"foo.api.public.latest",
+		"foo.api.system.latest",
 		"foo.stubs",
 		"foo.stubs.source",
 		"foo.stubs.source.system",
@@ -529,6 +533,8 @@
 
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
 		`dex2oatd`,
+		`sdklib-removed.api.public.latest`,
+		`sdklib.api.public.latest`,
 		`sdklib.impl`,
 		`sdklib.stubs`,
 		`sdklib.stubs.source`,
@@ -851,6 +857,8 @@
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
 		`dex2oatd`,
 		`prebuilt_sdklib`,
+		`sdklib-removed.api.public.latest`,
+		`sdklib.api.public.latest`,
 		`sdklib.impl`,
 		`sdklib.stubs`,
 		`sdklib.stubs.source`,
@@ -894,6 +902,8 @@
 
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
 		`prebuilt_sdklib`,
+		`sdklib-removed.api.public.latest`,
+		`sdklib.api.public.latest`,
 		`sdklib.impl`,
 		`sdklib.stubs`,
 		`sdklib.stubs.source`,
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 79d2ee9..a2cd261 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -24,15 +24,7 @@
 func init() {
 	registerSystemserverClasspathBuildComponents(android.InitRegistrationContext)
 
-	android.RegisterSdkMemberType(&systemServerClasspathFragmentMemberType{
-		SdkMemberTypeBase: android.SdkMemberTypeBase{
-			PropertyName: "systemserverclasspath_fragments",
-			SupportsSdk:  true,
-
-			// This was only added in Tiramisu.
-			SupportedBuildReleaseSpecification: "Tiramisu+",
-		},
-	})
+	android.RegisterSdkMemberType(SystemServerClasspathFragmentSdkMemberType)
 }
 
 func registerSystemserverClasspathBuildComponents(ctx android.RegistrationContext) {
@@ -41,6 +33,17 @@
 	ctx.RegisterModuleType("prebuilt_systemserverclasspath_fragment", prebuiltSystemServerClasspathModuleFactory)
 }
 
+var SystemServerClasspathFragmentSdkMemberType = &systemServerClasspathFragmentMemberType{
+	SdkMemberTypeBase: android.SdkMemberTypeBase{
+		PropertyName: "systemserverclasspath_fragments",
+		SupportsSdk:  true,
+
+		// Support for adding systemserverclasspath_fragments to the sdk snapshot was only added in
+		// Tiramisu.
+		SupportedBuildReleaseSpecification: "Tiramisu+",
+	},
+}
+
 type platformSystemServerClasspathModule struct {
 	android.ModuleBase
 
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 4773579..a37d259 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -84,6 +84,16 @@
     ],
 }
 
+python_test_host {
+    name: "jsonmodify_test",
+    main: "jsonmodify_test.py",
+    srcs: [
+        "jsonmodify_test.py",
+        "jsonmodify.py",
+    ],
+    test_suites: ["general-tests"],
+}
+
 python_binary_host {
     name: "test_config_fixer",
     main: "test_config_fixer.py",
diff --git a/scripts/jsonmodify.py b/scripts/jsonmodify.py
index ba1109e..8bd8d45 100755
--- a/scripts/jsonmodify.py
+++ b/scripts/jsonmodify.py
@@ -59,6 +59,13 @@
       cur[key] = val
 
 
+class ReplaceIfEqual(str):
+  def apply(self, obj, old_val, new_val):
+    cur, key = follow_path(obj, self)
+    if cur and cur[key] == int(old_val):
+      cur[key] = new_val
+
+
 class Remove(str):
   def apply(self, obj):
     cur, key = follow_path(obj, self)
@@ -75,6 +82,14 @@
       raise ValueError(self + " should be a array.")
     cur[key].extend(args)
 
+# A JSONDecoder that supports line comments start with //
+class JSONWithCommentsDecoder(json.JSONDecoder):
+  def __init__(self, **kw):
+    super().__init__(**kw)
+
+  def decode(self, s: str):
+    s = '\n'.join(l for l in s.split('\n') if not l.lstrip(' ').startswith('//'))
+    return super().decode(s)
 
 def main():
   parser = argparse.ArgumentParser()
@@ -91,6 +106,11 @@
                       help='replace value of the key specified by path. If path doesn\'t exist, no op.',
                       metavar=('path', 'value'),
                       nargs=2, dest='patch', action='append')
+  parser.add_argument("-se", "--replace-if-equal", type=ReplaceIfEqual,
+                      help='replace value of the key specified by path to new_value if it\'s equal to old_value.' +
+                      'If path doesn\'t exist or the value is not equal to old_value, no op.',
+                      metavar=('path', 'old_value', 'new_value'),
+                      nargs=3, dest='patch', action='append')
   parser.add_argument("-r", "--remove", type=Remove,
                       help='remove the key specified by path. If path doesn\'t exist, no op.',
                       metavar='path',
@@ -103,9 +123,9 @@
 
   if args.input:
     with open(args.input) as f:
-      obj = json.load(f, object_pairs_hook=collections.OrderedDict)
+      obj = json.load(f, object_pairs_hook=collections.OrderedDict, cls=JSONWithCommentsDecoder)
   else:
-    obj = json.load(sys.stdin, object_pairs_hook=collections.OrderedDict)
+    obj = json.load(sys.stdin, object_pairs_hook=collections.OrderedDict, cls=JSONWithCommentsDecoder)
 
   for p in args.patch:
     p[0].apply(obj, *p[1:])
diff --git a/scripts/jsonmodify_test.py b/scripts/jsonmodify_test.py
new file mode 100644
index 0000000..6f0291d
--- /dev/null
+++ b/scripts/jsonmodify_test.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 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.
+#
+"""Tests for jsonmodify."""
+
+import json
+import jsonmodify
+import unittest
+
+
+class JsonmodifyTest(unittest.TestCase):
+
+  def test_set_value(self):
+    obj = json.loads('{"field1": 111}')
+    field1 = jsonmodify.SetValue("field1")
+    field1.apply(obj, 222)
+    field2 = jsonmodify.SetValue("field2")
+    field2.apply(obj, 333)
+    expected = json.loads('{"field1": 222, "field2": 333}')
+    self.assertEqual(obj, expected)
+
+  def test_replace(self):
+    obj = json.loads('{"field1": 111}')
+    field1 = jsonmodify.Replace("field1")
+    field1.apply(obj, 222)
+    field2 = jsonmodify.Replace("field2")
+    field2.apply(obj, 333)
+    expected = json.loads('{"field1": 222}')
+    self.assertEqual(obj, expected)
+
+  def test_replace_if_equal(self):
+    obj = json.loads('{"field1": 111, "field2": 222}')
+    field1 = jsonmodify.ReplaceIfEqual("field1")
+    field1.apply(obj, 111, 333)
+    field2 = jsonmodify.ReplaceIfEqual("field2")
+    field2.apply(obj, 444, 555)
+    field3 = jsonmodify.ReplaceIfEqual("field3")
+    field3.apply(obj, 666, 777)
+    expected = json.loads('{"field1": 333, "field2": 222}')
+    self.assertEqual(obj, expected)
+
+  def test_remove(self):
+    obj = json.loads('{"field1": 111, "field2": 222}')
+    field2 = jsonmodify.Remove("field2")
+    field2.apply(obj)
+    field3 = jsonmodify.Remove("field3")
+    field3.apply(obj)
+    expected = json.loads('{"field1": 111}')
+    self.assertEqual(obj, expected)
+
+  def test_append_list(self):
+    obj = json.loads('{"field1": [111]}')
+    field1 = jsonmodify.AppendList("field1")
+    field1.apply(obj, 222, 333)
+    field2 = jsonmodify.AppendList("field2")
+    field2.apply(obj, 444, 555, 666)
+    expected = json.loads('{"field1": [111, 222, 333], "field2": [444, 555, 666]}')
+    self.assertEqual(obj, expected)
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 2dacdb5..13ddbe7 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -101,6 +101,9 @@
 				image_name: "art",
 				contents: ["mybootlib"],
 				apex_available: ["com.android.art"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			apex_key {
@@ -124,7 +127,7 @@
 	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("com.android.art", "mybootclasspathfragment")
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_bootclasspath_fragment {
@@ -152,41 +155,6 @@
     jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-prebuilt_bootclasspath_fragment {
-    name: "mysdk_mybootclasspathfragment@current",
-    sdk_member_name: "mybootclasspathfragment",
-    visibility: ["//visibility:public"],
-    apex_available: ["com.android.art"],
-    image_name: "art",
-    contents: ["mysdk_mybootlib@current"],
-    hidden_api: {
-        annotation_flags: "hiddenapi/annotation-flags.csv",
-        metadata: "hiddenapi/metadata.csv",
-        index: "hiddenapi/index.csv",
-        signature_patterns: "hiddenapi/signature-patterns.csv",
-        filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
-        filtered_flags: "hiddenapi/filtered-flags.csv",
-    },
-}
-
-java_import {
-    name: "mysdk_mybootlib@current",
-    sdk_member_name: "mybootlib",
-    visibility: ["//visibility:public"],
-    apex_available: ["com.android.art"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    bootclasspath_fragments: ["mysdk_mybootclasspathfragment@current"],
-    java_boot_libs: ["mysdk_mybootlib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
@@ -222,7 +190,7 @@
 	android.AssertStringDoesContain(t, "boot jars package check", command, expectedCommandArgs)
 }
 
-func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
+func testSnapshotWithBootClasspathFragment_Contents(t *testing.T, sdk string, copyRules string) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithJavaDefaultModules,
@@ -234,19 +202,7 @@
 		// Add a platform_bootclasspath that depends on the fragment.
 		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
 
-		android.FixtureWithRootAndroidBp(`
-			sdk {
-				name: "mysdk",
-				bootclasspath_fragments: ["mybootclasspathfragment"],
-				java_sdk_libs: [
-					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
-					// a java_sdk_libs module because it is used in the mybootclasspathfragment's
-					// api.stub_libs property. However, it is specified here to ensure that duplicates are
-					// correctly deduped.
-					"mysdklibrary",
-				],
-			}
-
+		android.FixtureWithRootAndroidBp(sdk+`
 			apex {
 				name: "myapex",
 				key: "myapex.key",
@@ -270,6 +226,9 @@
 					// This should be automatically added to the sdk_snapshot as a java_sdk_libs module.
 					stub_libs: ["mycoreplatform"],
 				},
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_library {
@@ -317,7 +276,7 @@
 	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_bootclasspath_fragment {
@@ -402,121 +361,7 @@
     },
 }
 		`),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-prebuilt_bootclasspath_fragment {
-    name: "mysdk_mybootclasspathfragment@current",
-    sdk_member_name: "mybootclasspathfragment",
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    contents: [
-        "mysdk_mybootlib@current",
-        "mysdk_myothersdklibrary@current",
-    ],
-    api: {
-        stub_libs: ["mysdk_mysdklibrary@current"],
-    },
-    core_platform_api: {
-        stub_libs: ["mysdk_mycoreplatform@current"],
-    },
-    hidden_api: {
-        annotation_flags: "hiddenapi/annotation-flags.csv",
-        metadata: "hiddenapi/metadata.csv",
-        index: "hiddenapi/index.csv",
-        signature_patterns: "hiddenapi/signature-patterns.csv",
-        filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
-        filtered_flags: "hiddenapi/filtered-flags.csv",
-    },
-}
-
-java_import {
-    name: "mysdk_mybootlib@current",
-    sdk_member_name: "mybootlib",
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
-    permitted_packages: ["mybootlib"],
-}
-
-java_sdk_library_import {
-    name: "mysdk_myothersdklibrary@current",
-    sdk_member_name: "myothersdklibrary",
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    shared_library: true,
-    compile_dex: true,
-    permitted_packages: ["myothersdklibrary"],
-    public: {
-        jars: ["sdk_library/public/myothersdklibrary-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"],
-        current_api: "sdk_library/public/myothersdklibrary.txt",
-        removed_api: "sdk_library/public/myothersdklibrary-removed.txt",
-        sdk_version: "current",
-    },
-}
-
-java_sdk_library_import {
-    name: "mysdk_mysdklibrary@current",
-    sdk_member_name: "mysdklibrary",
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    shared_library: false,
-    public: {
-        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
-        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
-        current_api: "sdk_library/public/mysdklibrary.txt",
-        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
-        sdk_version: "current",
-    },
-}
-
-java_sdk_library_import {
-    name: "mysdk_mycoreplatform@current",
-    sdk_member_name: "mycoreplatform",
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    shared_library: true,
-    compile_dex: true,
-    public: {
-        jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
-        stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"],
-        current_api: "sdk_library/public/mycoreplatform.txt",
-        removed_api: "sdk_library/public/mycoreplatform-removed.txt",
-        sdk_version: "current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    bootclasspath_fragments: ["mysdk_mybootclasspathfragment@current"],
-    java_boot_libs: ["mysdk_mybootlib@current"],
-    java_sdk_libs: [
-        "mysdk_myothersdklibrary@current",
-        "mysdk_mysdklibrary@current",
-        "mysdk_mycoreplatform@current",
-    ],
-}
-		`),
-		checkAllCopyRules(`
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
-.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
-.intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
-.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
-.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
-.intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
-.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
-.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
-.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
-`),
+		checkAllCopyRules(copyRules),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
 		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
 			module := result.ModuleForTests("platform-bootclasspath", "android_common")
@@ -553,6 +398,89 @@
 	)
 }
 
+func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
+	t.Run("added-directly", func(t *testing.T) {
+		testSnapshotWithBootClasspathFragment_Contents(t, `
+			sdk {
+				name: "mysdk",
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+				java_sdk_libs: [
+					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+					// a java_sdk_libs module because it is used in the mybootclasspathfragment's
+					// api.stub_libs property. However, it is specified here to ensure that duplicates are
+					// correctly deduped.
+					"mysdklibrary",
+				],
+			}
+		`, `
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
+.intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
+.intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
+.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
+.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
+.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
+`)
+	})
+
+	copyBootclasspathFragmentFromApexVariantRules := `
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/index.csv -> hiddenapi/index.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
+.intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
+.intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
+.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
+.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
+.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
+`
+	t.Run("added-via-apex", func(t *testing.T) {
+		testSnapshotWithBootClasspathFragment_Contents(t, `
+			sdk {
+				name: "mysdk",
+				apexes: ["myapex"],
+			}
+		`, copyBootclasspathFragmentFromApexVariantRules)
+	})
+
+	t.Run("added-directly-and-indirectly", func(t *testing.T) {
+		testSnapshotWithBootClasspathFragment_Contents(t, `
+			sdk {
+				name: "mysdk",
+				apexes: ["myapex"],
+				// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+				// a bootclasspath_fragments module because it is used in the myapex's
+				// bootclasspath_fragments property. However, it is specified here to ensure that duplicates
+				// are correctly deduped.
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+				java_sdk_libs: [
+					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+					// a java_sdk_libs module because it is used in the mybootclasspathfragment's
+					// api.stub_libs property. However, it is specified here to ensure that duplicates are
+					// correctly deduped.
+					"mysdklibrary",
+				],
+			}
+		`, copyBootclasspathFragmentFromApexVariantRules)
+	})
+}
+
 // TestSnapshotWithBootClasspathFragment_Fragments makes sure that the fragments property of a
 // bootclasspath_fragment is correctly output to the sdk snapshot.
 func TestSnapshotWithBootClasspathFragment_Fragments(t *testing.T) {
@@ -585,6 +513,9 @@
 				contents: [
 					"myotherlib",
 				],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_library {
@@ -614,6 +545,9 @@
 						module: "myotherbootclasspathfragment"
 					},
 				],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_sdk_library {
@@ -630,7 +564,7 @@
 	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_bootclasspath_fragment {
@@ -693,6 +627,9 @@
 			image_name: "art",
 			contents: ["mybootlib"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -800,6 +737,7 @@
 					unsupported_packages: [
 							"my-unsupported-packages.txt",
 					],
+					split_packages: ["*"],
 				},
 			}
 
@@ -828,7 +766,7 @@
 	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_bootclasspath_fragment {
diff --git a/sdk/bp.go b/sdk/bp.go
index e2dace8..7ff85a1 100644
--- a/sdk/bp.go
+++ b/sdk/bp.go
@@ -298,15 +298,15 @@
 	return module
 }
 
-func (t identityTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t identityTransformation) transformPropertySetBeforeContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
 	return propertySet, tag
 }
 
-func (t identityTransformation) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t identityTransformation) transformPropertySetAfterContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
 	return propertySet, tag
 }
 
-func (t identityTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+func (t identityTransformation) transformProperty(_ string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
 	return value, tag
 }
 
@@ -332,7 +332,7 @@
 	return &moduleCopy
 }
 
-func (t deepCopyTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t deepCopyTransformation) transformPropertySetBeforeContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
 	// Create a shallow copy of the properties map. Any mutable property values will be copied by the
 	// transformer.
 	propertiesCopy := make(map[string]interface{})
@@ -354,7 +354,7 @@
 	}, tag
 }
 
-func (t deepCopyTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+func (t deepCopyTransformation) transformProperty(_ string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
 	// Copy string slice, otherwise return value.
 	if values, ok := value.([]string); ok {
 		valuesCopy := make([]string, len(values))
@@ -372,7 +372,7 @@
 	order   []*bpModule
 }
 
-// Add a module.
+// AddModule adds a module to this.
 //
 // The module must have had its "name" property set to a string value that
 // is unique within this file.
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 571d214..265579a 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -120,7 +120,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -145,48 +145,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_sdkmember@current",
-    sdk_member_name: "sdkmember",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "64",
-    target: {
-        host: {
-            enabled: false,
-        },
-        android_arm64: {
-            srcs: ["android/arm64/lib/sdkmember.so"],
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/lib/sdkmember.so"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    compile_multilib: "64",
-    native_shared_libs: ["mysdk_sdkmember@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/sdkmember/android_arm64_armv8-a_shared/sdkmember.so -> android/arm64/lib/sdkmember.so
 .intermediates/sdkmember/linux_glibc_x86_64_shared/sdkmember.so -> linux_glibc/x86_64/lib/sdkmember.so
@@ -265,7 +223,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_object {
@@ -289,37 +247,6 @@
     },
 }
 `),
-		// Make sure that the generated sdk_snapshot uses the native_objects property.
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_object {
-    name: "mysdk_crtobj@current",
-    sdk_member_name: "crtobj",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    stl: "none",
-    compile_multilib: "both",
-    system_shared_libs: [],
-    sanitize: {
-        never: true,
-    },
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/crtobj.o"],
-        },
-        arm: {
-            srcs: ["arm/lib/crtobj.o"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    native_objects: ["mysdk_crtobj@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/crtobj/android_arm64_armv8-a/crtobj.o -> arm64/lib/crtobj.o
 .intermediates/crtobj/android_arm_armv7-a-neon/crtobj.o -> arm/lib/crtobj.o
@@ -403,7 +330,7 @@
 	errorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module source path "snapshot/include_gen/generated_foo/gen/protos" does not exist`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -476,7 +403,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -535,7 +462,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mymodule_exports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
@@ -554,33 +481,6 @@
     },
 }
 `),
-		// Make sure that the generated sdk_snapshot uses the native_binaries property.
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
-    name: "mymodule_exports_mynativebinary@current",
-    sdk_member_name: "mynativebinary",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    compile_multilib: "both",
-    arch: {
-        arm64: {
-            srcs: ["arm64/bin/mynativebinary"],
-        },
-        arm: {
-            srcs: ["arm/bin/mynativebinary"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "mymodule_exports@current",
-    visibility: ["//visibility:public"],
-    native_binaries: ["mymodule_exports_mynativebinary@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mynativebinary/android_arm64_armv8-a/mynativebinary -> arm64/bin/mynativebinary
 .intermediates/mynativebinary/android_arm_armv7-a-neon/mynativebinary -> arm/bin/mynativebinary
@@ -620,7 +520,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
@@ -656,68 +556,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
-    name: "myexports_mynativebinary@current",
-    sdk_member_name: "mynativebinary",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc: {
-            compile_multilib: "both",
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/bin/mynativebinary"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["linux_glibc/x86/bin/mynativebinary"],
-        },
-        windows: {
-            compile_multilib: "64",
-        },
-        windows_x86_64: {
-            enabled: true,
-            srcs: ["windows/x86_64/bin/mynativebinary.exe"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_binaries: ["myexports_mynativebinary@current"],
-    target: {
-        windows: {
-            compile_multilib: "64",
-        },
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-        windows_x86_64: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mynativebinary/linux_glibc_x86_64/mynativebinary -> linux_glibc/x86_64/bin/mynativebinary
 .intermediates/mynativebinary/linux_glibc_x86/mynativebinary -> linux_glibc/x86/bin/mynativebinary
@@ -780,7 +618,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
@@ -823,69 +661,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
-    name: "myexports_mynativebinary@current",
-    sdk_member_name: "mynativebinary",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "64",
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_bionic_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/bin/mynativebinary"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
-    name: "myexports_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "64",
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_bionic_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.so"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    compile_multilib: "64",
-    native_binaries: ["myexports_mynativebinary@current"],
-    native_shared_libs: ["myexports_mynativelib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_bionic_x86_64: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mynativebinary/linux_bionic_x86_64/mynativebinary -> x86_64/bin/mynativebinary
 .intermediates/mynativelib/linux_bionic_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so
@@ -918,7 +693,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mymodule_exports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
@@ -947,55 +722,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
-    name: "mymodule_exports_linker@current",
-    sdk_member_name: "linker",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "both",
-    static_executable: true,
-    nocrt: true,
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/bin/linker"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["x86/bin/linker"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "mymodule_exports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_binaries: ["mymodule_exports_linker@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/linker/linux_glibc_x86_64/linker -> x86_64/bin/linker
 .intermediates/linker/linux_glibc_x86/linker -> x86/bin/linker
@@ -1026,7 +752,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -1127,7 +853,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -1224,7 +950,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -1255,57 +981,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    sdk_version: "minimum",
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/myinclude"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.so"],
-            export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["x86/lib/mynativelib.so"],
-            export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_shared_libs: ["mysdk_mynativelib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so
@@ -1351,7 +1026,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -1387,68 +1062,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc: {
-            compile_multilib: "both",
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["linux_glibc/x86/lib/mynativelib.so"],
-        },
-        windows: {
-            compile_multilib: "64",
-        },
-        windows_x86_64: {
-            enabled: true,
-            srcs: ["windows/x86_64/lib/mynativelib.dll"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_shared_libs: ["mysdk_mynativelib@current"],
-    target: {
-        windows: {
-            compile_multilib: "64",
-        },
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-        windows_x86_64: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
 .intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
@@ -1479,7 +1092,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_static {
@@ -1542,7 +1155,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_static {
@@ -1572,56 +1185,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_static {
-    name: "myexports_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/myinclude"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.a"],
-            export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["x86/lib/mynativelib.a"],
-            export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_static_libs: ["myexports_mynativelib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a
@@ -1656,7 +1219,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library {
@@ -1688,46 +1251,6 @@
     },
 }
 `),
-		// Make sure that the generated sdk_snapshot uses the native_libs property.
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library {
-    name: "myexports_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    vendor_available: true,
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/myinclude"],
-    arch: {
-        arm64: {
-            static: {
-                srcs: ["arm64/lib/mynativelib.a"],
-            },
-            shared: {
-                srcs: ["arm64/lib/mynativelib.so"],
-            },
-        },
-        arm: {
-            static: {
-                srcs: ["arm/lib/mynativelib.a"],
-            },
-            shared: {
-                srcs: ["arm/lib/mynativelib.so"],
-            },
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    native_libs: ["myexports_mynativelib@current"],
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
@@ -1740,6 +1263,229 @@
 	)
 }
 
+func TestSnapshotSameLibraryWithNativeLibsAndNativeSharedLib(t *testing.T) {
+	result := testSdkWithCc(t, `
+		module_exports {
+			host_supported: true,
+			name: "myexports",
+			target: {
+				android: {
+						native_shared_libs: [
+								"mynativelib",
+						],
+				},
+				not_windows: {
+						native_libs: [
+								"mynativelib",
+						],
+				},
+			},
+		}
+
+		cc_library {
+			name: "mynativelib",
+			host_supported: true,
+			srcs: [
+				"Test.cpp",
+			],
+			stl: "none",
+			recovery_available: true,
+			vendor_available: true,
+		}
+	`)
+
+	CheckSnapshot(t, result, "myexports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library {
+    name: "mynativelib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    host_supported: true,
+    vendor_available: true,
+    stl: "none",
+    compile_multilib: "both",
+    target: {
+        host: {
+            enabled: false,
+        },
+        android_arm64: {
+            shared: {
+                srcs: ["android/arm64/lib/mynativelib.so"],
+            },
+            static: {
+                enabled: false,
+            },
+        },
+        android_arm: {
+            shared: {
+                srcs: ["android/arm/lib/mynativelib.so"],
+            },
+            static: {
+                enabled: false,
+            },
+        },
+        linux_glibc_x86_64: {
+            enabled: true,
+            static: {
+                srcs: ["linux_glibc/x86_64/lib/mynativelib.a"],
+            },
+            shared: {
+                srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+            },
+        },
+        linux_glibc_x86: {
+            enabled: true,
+            static: {
+                srcs: ["linux_glibc/x86/lib/mynativelib.a"],
+            },
+            shared: {
+                srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+            },
+        },
+    },
+}
+`),
+		checkAllCopyRules(`
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> android/arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> android/arm/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> linux_glibc/x86_64/lib/mynativelib.a
+.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_static/mynativelib.a -> linux_glibc/x86/lib/mynativelib.a
+.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
+`),
+	)
+}
+
+func TestSnapshotSameLibraryWithAndroidNativeLibsAndHostNativeSharedLib(t *testing.T) {
+	result := testSdkWithCc(t, `
+		module_exports {
+			host_supported: true,
+			name: "myexports",
+			target: {
+				android: {
+						native_libs: [
+								"mynativelib",
+						],
+				},
+				not_windows: {
+						native_shared_libs: [
+								"mynativelib",
+						],
+				},
+			},
+		}
+
+		cc_library {
+			name: "mynativelib",
+			host_supported: true,
+			srcs: [
+				"Test.cpp",
+			],
+			stl: "none",
+			recovery_available: true,
+			vendor_available: true,
+		}
+	`)
+
+	CheckSnapshot(t, result, "myexports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library {
+    name: "mynativelib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    host_supported: true,
+    vendor_available: true,
+    stl: "none",
+    compile_multilib: "both",
+    target: {
+        host: {
+            enabled: false,
+        },
+        android_arm64: {
+            static: {
+                srcs: ["android/arm64/lib/mynativelib.a"],
+            },
+            shared: {
+                srcs: ["android/arm64/lib/mynativelib.so"],
+            },
+        },
+        android_arm: {
+            static: {
+                srcs: ["android/arm/lib/mynativelib.a"],
+            },
+            shared: {
+                srcs: ["android/arm/lib/mynativelib.so"],
+            },
+        },
+        linux_glibc_x86_64: {
+            enabled: true,
+            shared: {
+                srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+            },
+            static: {
+                enabled: false,
+            },
+        },
+        linux_glibc_x86: {
+            enabled: true,
+            shared: {
+                srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+            },
+            static: {
+                enabled: false,
+            },
+        },
+    },
+}
+`),
+		checkAllCopyRules(`
+.intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> android/arm64/lib/mynativelib.a
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> android/arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> android/arm/lib/mynativelib.a
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> android/arm/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
+`),
+	)
+}
+
+func TestSnapshotSameLibraryWithNativeStaticLibsAndNativeSharedLib(t *testing.T) {
+	testSdkError(t, "Incompatible member types", `
+		module_exports {
+			host_supported: true,
+			name: "myexports",
+			target: {
+				android: {
+						native_shared_libs: [
+								"mynativelib",
+						],
+				},
+				not_windows: {
+						native_static_libs: [
+								"mynativelib",
+						],
+				},
+			},
+		}
+
+		cc_library {
+			name: "mynativelib",
+			host_supported: true,
+			srcs: [
+			],
+			stl: "none",
+			recovery_available: true,
+			vendor_available: true,
+		}
+	`)
+}
+
 func TestHostSnapshotWithMultiLib64(t *testing.T) {
 	result := testSdkWithCc(t, `
 		module_exports {
@@ -1771,7 +1517,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_static {
@@ -1798,51 +1544,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_static {
-    name: "myexports_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "64",
-    export_include_dirs: [
-        "include/myinclude",
-        "include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl",
-    ],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.a"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    compile_multilib: "64",
-    native_static_libs: ["myexports_mynativelib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h
@@ -1868,7 +1569,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
@@ -1912,7 +1613,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
@@ -1997,7 +1698,7 @@
 	`, trait, property))
 
 		CheckSnapshot(t, result, "mysdk", "",
-			checkUnversionedAndroidBpContents(fmt.Sprintf(`
+			checkAndroidBpContents(fmt.Sprintf(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
@@ -2046,7 +1747,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
@@ -2072,51 +1773,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_headers {
-    name: "mysdk_mynativeheaders@current",
-    sdk_member_name: "mynativeheaders",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/myinclude"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_header_libs: ["mysdk_mynativeheaders@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 `),
@@ -2148,7 +1804,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
@@ -2179,55 +1835,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_headers {
-    name: "mysdk_mynativeheaders@current",
-    sdk_member_name: "mynativeheaders",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    stl: "none",
-    compile_multilib: "both",
-    export_system_include_dirs: ["common_os/include/myinclude"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        android: {
-            export_include_dirs: ["android/include/myinclude-android"],
-        },
-        linux_glibc: {
-            export_include_dirs: ["linux_glibc/include/myinclude-host"],
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    native_header_libs: ["mysdk_mynativeheaders@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> common_os/include/myinclude/Test.h
 myinclude-android/AndroidTest.h -> android/include/myinclude-android/AndroidTest.h
@@ -2260,7 +1867,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -2333,7 +1940,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -2367,59 +1974,7 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_sslvariants@current",
-    sdk_member_name: "sslvariants",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    installable: false,
-    compile_multilib: "both",
-    target: {
-        host: {
-            enabled: false,
-        },
-        android: {
-            system_shared_libs: [],
-        },
-        android_arm64: {
-            srcs: ["android/arm64/lib/sslvariants.so"],
-        },
-        android_arm: {
-            srcs: ["android/arm/lib/sslvariants.so"],
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/lib/sslvariants.so"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["linux_glibc/x86/lib/sslvariants.so"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    native_shared_libs: ["mysdk_sslvariants@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`))
+	)
 }
 
 func TestStubsLibrary(t *testing.T) {
@@ -2444,7 +1999,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -2498,7 +2053,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -2537,64 +2092,7 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_stubslib@current",
-    sdk_member_name: "stubslib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    installable: false,
-    compile_multilib: "both",
-    stubs: {
-        versions: [
-            "1",
-            "2",
-            "3",
-            "current",
-        ],
-    },
-    target: {
-        host: {
-            enabled: false,
-        },
-        android_arm64: {
-            srcs: ["android/arm64/lib/stubslib.so"],
-        },
-        android_arm: {
-            srcs: ["android/arm/lib/stubslib.so"],
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/lib/stubslib.so"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["linux_glibc/x86/lib/stubslib.so"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    native_shared_libs: ["mysdk_stubslib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`))
+	)
 }
 
 func TestUniqueHostSoname(t *testing.T) {
@@ -2613,7 +2111,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -2645,57 +2143,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_mylib@current",
-    sdk_member_name: "mylib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    installable: false,
-    unique_host_soname: true,
-    compile_multilib: "both",
-    target: {
-        host: {
-            enabled: false,
-        },
-        android_arm64: {
-            srcs: ["android/arm64/lib/mylib.so"],
-        },
-        android_arm: {
-            srcs: ["android/arm/lib/mylib.so"],
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/lib/mylib-host.so"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["linux_glibc/x86/lib/mylib-host.so"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    native_shared_libs: ["mysdk_mylib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mylib/android_arm64_armv8-a_shared/mylib.so -> android/arm64/lib/mylib.so
 .intermediates/mylib/android_arm_armv7-a-neon_shared/mylib.so -> android/arm/lib/mylib.so
@@ -2728,7 +2175,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
index 00073c2..d166add 100644
--- a/sdk/compat_config_sdk_test.go
+++ b/sdk/compat_config_sdk_test.go
@@ -37,23 +37,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-prebuilt_platform_compat_config {
-    name: "mysdk_myconfig@current",
-    sdk_member_name: "myconfig",
-    visibility: ["//visibility:public"],
-    metadata: "compat_configs/myconfig/myconfig_meta.xml",
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    compat_configs: ["mysdk_myconfig@current"],
-}
-`),
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_platform_compat_config {
diff --git a/sdk/exports.go b/sdk/exports.go
index 9a0ba4e..7645d3f 100644
--- a/sdk/exports.go
+++ b/sdk/exports.go
@@ -31,7 +31,7 @@
 	return newSdkModule(true)
 }
 
-// module_exports_snapshot is a versioned snapshot of prebuilt versions of all the exports
+// module_exports_snapshot is a snapshot of prebuilt versions of all the exports
 // of a mainline module.
 func ModuleExportsSnapshotsFactory() android.Module {
 	s := newSdkModule(true)
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
index 17ddf17..2605fd1 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -43,7 +43,7 @@
 		})
 
 	CheckSnapshot(t, result, "myexports", "package",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -54,22 +54,5 @@
     jars: ["java/myjavalib.jar"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    java_libs: ["myexports_myjavalib@current"],
-}
-`),
 	)
 }
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 2cadd60..57c171f 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -97,7 +97,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -109,24 +109,6 @@
     permitted_packages: ["pkg.myjavalib"],
 }
 `),
-		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: ["java/myjavalib.jar"],
-    permitted_packages: ["pkg.myjavalib"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/myjavalib.jar
 aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -161,7 +143,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -174,27 +156,6 @@
     jars: ["java/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"],
-    device_supported: false,
-    host_supported: true,
-    jars: ["java/myjavalib.jar"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
 aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -221,7 +182,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -240,32 +201,6 @@
     },
 }
 `),
-		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"],
-    host_supported: true,
-    target: {
-        android: {
-            jars: ["java/android/myjavalib.jar"],
-        },
-        linux_glibc: {
-            jars: ["java/linux_glibc/myjavalib.jar"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/android/myjavalib.jar
 .intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/linux_glibc/myjavalib.jar
@@ -299,7 +234,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -310,23 +245,6 @@
     jars: ["java/myjavalib.jar"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    java_libs: ["myexports_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/android_common/withres/myjavalib.jar -> java/myjavalib.jar
 aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -362,7 +280,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -374,25 +292,6 @@
     permitted_packages: ["pkg.myjavalib"],
 }
 `),
-		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: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
-    permitted_packages: ["pkg.myjavalib"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_boot_libs: ["mysdk_myjavalib@current"],
-}
-
-`),
 		checkAllCopyRules(`
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
 `),
@@ -424,7 +323,7 @@
 	`)
 
 		CheckSnapshot(t, result, "mysdk", "",
-			checkUnversionedAndroidBpContents(fmt.Sprintf(`
+			checkAndroidBpContents(fmt.Sprintf(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -482,7 +381,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -494,24 +393,6 @@
     permitted_packages: ["pkg.myjavalib"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java_systemserver_libs/snapshot/jars/are/invalid/myjavalib.jar"],
-    permitted_packages: ["pkg.myjavalib"],
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    java_systemserver_libs: ["myexports_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myexports/common_os/empty -> java_systemserver_libs/snapshot/jars/are/invalid/myjavalib.jar
 `),
@@ -545,7 +426,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -558,27 +439,6 @@
     jars: ["java/myjavalib.jar"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    jars: ["java/myjavalib.jar"],
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    java_libs: ["myexports_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
 aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -604,7 +464,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_test_import {
@@ -616,24 +476,6 @@
     test_config: "java/myjavatests-AndroidTest.xml",
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_test_import {
-    name: "myexports_myjavatests@current",
-    sdk_member_name: "myjavatests",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavatests.jar"],
-    test_config: "java/myjavatests-AndroidTest.xml",
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    java_tests: ["myexports_myjavatests@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavatests/android_common/javac/myjavatests.jar -> java/myjavatests.jar
 .intermediates/myjavatests/android_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
@@ -662,7 +504,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_test_import {
@@ -676,28 +518,6 @@
     test_config: "java/myjavatests-AndroidTest.xml",
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_test_import {
-    name: "myexports_myjavatests@current",
-    sdk_member_name: "myjavatests",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    jars: ["java/myjavatests.jar"],
-    test_config: "java/myjavatests-AndroidTest.xml",
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    java_tests: ["myexports_myjavatests@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavatests/linux_glibc_common/javac/myjavatests.jar -> java/myjavatests.jar
 .intermediates/myjavatests/linux_glibc_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
@@ -706,7 +526,19 @@
 }
 
 func TestSnapshotWithJavaSystemModules(t *testing.T) {
-	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		java.PrepareForTestWithJavaDefaultModules,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithPrebuiltApisAndExtensions(map[string][]string{
+			"31":      {"myjavalib"},
+			"32":      {"myjavalib"},
+			"current": {"myjavalib"},
+		}, map[string][]string{
+			"1": {"myjavalib"},
+			"2": {"myjavalib"},
+		}),
+	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_header_libs: ["exported-system-module"],
@@ -746,7 +578,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -791,59 +623,6 @@
     ],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_exported-system-module@current",
-    sdk_member_name: "exported-system-module",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/exported-system-module.jar"],
-}
-
-java_import {
-    name: "mysdk_system-module@current",
-    sdk_member_name: "system-module",
-    visibility: ["//visibility:private"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/system-module.jar"],
-}
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    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",
-        sdk_version: "current",
-    },
-}
-
-java_system_modules_import {
-    name: "mysdk_my-system-modules@current",
-    sdk_member_name: "my-system-modules",
-    visibility: ["//visibility:public"],
-    libs: [
-        "mysdk_system-module@current",
-        "mysdk_exported-system-module@current",
-        "mysdk_myjavalib.stubs@current",
-    ],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_exported-system-module@current"],
-    java_sdk_libs: ["mysdk_myjavalib@current"],
-    java_system_modules: ["mysdk_my-system-modules@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/exported-system-module/android_common/turbine-combined/exported-system-module.jar -> java/exported-system-module.jar
 .intermediates/system-module/android_common/turbine-combined/system-module.jar -> java/system-module.jar
@@ -851,6 +630,53 @@
 .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
 `),
+		checkInfoContents(result.Config, `
+[
+  {
+    "@type": "sdk",
+    "@name": "mysdk",
+    "java_header_libs": [
+      "exported-system-module",
+      "system-module"
+    ],
+    "java_sdk_libs": [
+      "myjavalib"
+    ],
+    "java_system_modules": [
+      "my-system-modules"
+    ]
+  },
+  {
+    "@type": "java_library",
+    "@name": "exported-system-module"
+  },
+  {
+    "@type": "java_system_modules",
+    "@name": "my-system-modules",
+    "@deps": [
+      "exported-system-module",
+      "system-module"
+    ]
+  },
+  {
+    "@type": "java_sdk_library",
+    "@name": "myjavalib",
+    "dist_stem": "myjavalib",
+    "scopes": {
+      "public": {
+        "current_api": "sdk_library/public/myjavalib.txt",
+        "latest_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib.api.public.latest/gen/myjavalib.api.public.latest",
+        "latest_removed_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib-removed.api.public.latest/gen/myjavalib-removed.api.public.latest",
+        "removed_api": "sdk_library/public/myjavalib-removed.txt"
+      }
+    }
+  },
+  {
+    "@type": "java_library",
+    "@name": "system-module"
+  }
+]
+`),
 	)
 }
 
@@ -881,7 +707,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -903,36 +729,6 @@
     libs: ["mysdk_system-module"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_system-module@current",
-    sdk_member_name: "system-module",
-    visibility: ["//visibility:private"],
-    apex_available: ["//apex_available:platform"],
-    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",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    libs: ["mysdk_system-module@current"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    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"),
 	)
 }
@@ -975,7 +771,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -1012,58 +808,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myexports_hostjavalib@current",
-    sdk_member_name: "hostjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    jars: ["java/hostjavalib.jar"],
-}
-
-java_import {
-    name: "myexports_androidjavalib@current",
-    sdk_member_name: "androidjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/androidjavalib.jar"],
-}
-
-java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    target: {
-        android: {
-            jars: ["java/android/myjavalib.jar"],
-        },
-        linux_glibc: {
-            jars: ["java/linux_glibc/myjavalib.jar"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    java_libs: ["myexports_myjavalib@current"],
-    target: {
-        android: {
-            java_header_libs: ["myexports_androidjavalib@current"],
-        },
-        linux_glibc: {
-            java_header_libs: ["myexports_hostjavalib@current"],
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/hostjavalib/linux_glibc_common/javac/hostjavalib.jar -> java/hostjavalib.jar
 .intermediates/androidjavalib/android_common/turbine-combined/androidjavalib.jar -> java/androidjavalib.jar
@@ -1093,7 +837,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1126,45 +870,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    shared_library: false,
-    permitted_packages: ["pkg.myjavalib"],
-    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",
-        sdk_version: "current",
-    },
-    system: {
-        jars: ["sdk_library/system/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
-        current_api: "sdk_library/system/myjavalib.txt",
-        removed_api: "sdk_library/system/myjavalib-removed.txt",
-        sdk_version: "system_current",
-    },
-    test: {
-        jars: ["sdk_library/test/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/test/myjavalib_stub_sources"],
-        current_api: "sdk_library/test/myjavalib.txt",
-        removed_api: "sdk_library/test/myjavalib-removed.txt",
-        sdk_version: "test_current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@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
@@ -1181,12 +886,6 @@
 			".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
 			".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip",
 		),
-		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
-			// Make sure that the name of the child modules created by a versioned java_sdk_library_import
-			// module is correct, i.e. the suffix is added before the version and not after.
-			result.Module("mysdk_myjavalib.stubs@current", "android_common")
-			result.Module("mysdk_myjavalib.stubs.source@current", "android_common")
-		}),
 	)
 }
 
@@ -1214,7 +913,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1261,7 +960,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1315,7 +1014,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1365,7 +1064,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1432,7 +1131,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1450,30 +1149,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    shared_library: true,
-    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",
-        sdk_version: "none",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@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
@@ -1504,7 +1179,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1522,30 +1197,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    shared_library: true,
-    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",
-        sdk_version: "module_current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@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
@@ -1579,7 +1230,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1604,37 +1255,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    shared_library: true,
-    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",
-        sdk_version: "current",
-    },
-    system: {
-        jars: ["sdk_library/system/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
-        current_api: "sdk_library/system/myjavalib.txt",
-        removed_api: "sdk_library/system/myjavalib-removed.txt",
-        sdk_version: "system_current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@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
@@ -1675,7 +1295,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1707,44 +1327,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    shared_library: true,
-    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",
-        sdk_version: "current",
-    },
-    system: {
-        jars: ["sdk_library/system/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
-        current_api: "sdk_library/system/myjavalib.txt",
-        removed_api: "sdk_library/system/myjavalib-removed.txt",
-        sdk_version: "system_current",
-    },
-    module_lib: {
-        jars: ["sdk_library/module-lib/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/module-lib/myjavalib_stub_sources"],
-        current_api: "sdk_library/module-lib/myjavalib.txt",
-        removed_api: "sdk_library/module-lib/myjavalib-removed.txt",
-        sdk_version: "module_current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@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
@@ -1786,7 +1368,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1811,37 +1393,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    shared_library: true,
-    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",
-        sdk_version: "current",
-    },
-    system_server: {
-        jars: ["sdk_library/system-server/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/system-server/myjavalib_stub_sources"],
-        current_api: "sdk_library/system-server/myjavalib.txt",
-        removed_api: "sdk_library/system-server/myjavalib-removed.txt",
-        sdk_version: "system_server_current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@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
@@ -1877,7 +1428,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1896,31 +1447,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    naming_scheme: "default",
-    shared_library: true,
-    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",
-        sdk_version: "current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@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
@@ -1959,7 +1485,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1978,31 +1504,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    shared_library: true,
-    doctag_files: ["doctags/docs/known_doctags"],
-    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",
-        sdk_version: "current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@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
diff --git a/sdk/license_sdk_test.go b/sdk/license_sdk_test.go
index 1ef6fe6..829edf1 100644
--- a/sdk/license_sdk_test.go
+++ b/sdk/license_sdk_test.go
@@ -60,7 +60,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 package {
@@ -91,44 +91,6 @@
     ],
 }
 		`),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-package {
-    // A default list here prevents the license LSC from adding its own list which would
-    // be unnecessary as every module in the sdk already has its own licenses property.
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    licenses: ["mysdk_mylicense@current"],
-    jars: ["java/myjavalib.jar"],
-}
-
-license {
-    name: "mysdk_mylicense@current",
-    sdk_member_name: "mylicense",
-    visibility: ["//visibility:private"],
-    license_kinds: [
-        "SPDX-license-identifier-Apache-2.0",
-        "legacy_unencumbered",
-    ],
-    license_text: [
-        "licenses/NOTICE1",
-        "licenses/NOTICE2",
-    ],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
-		`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/myjavalib.jar
 NOTICE1 -> licenses/NOTICE1
diff --git a/sdk/member_trait_test.go b/sdk/member_trait_test.go
index a3db189..99caf13 100644
--- a/sdk/member_trait_test.go
+++ b/sdk/member_trait_test.go
@@ -134,7 +134,7 @@
 	).RunTest(t)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -145,23 +145,6 @@
     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"],
-}
-`),
 	)
 }
 
@@ -216,7 +199,7 @@
 	).RunTest(t)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_test_import {
diff --git a/sdk/sdk.go b/sdk/sdk.go
index c8c7b79..aeeedb4 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -75,6 +75,8 @@
 
 	snapshotFile android.OptionalPath
 
+	infoFile android.OptionalPath
+
 	// The builder, preserved for testing.
 	builderForTests *snapshotBuilder
 }
@@ -144,7 +146,7 @@
 	return s
 }
 
-// sdk_snapshot is a versioned snapshot of an SDK. This is an auto-generated module.
+// sdk_snapshot is a snapshot of an SDK. This is an auto-generated module.
 func SnapshotModuleFactory() android.Module {
 	s := newSdkModule(false)
 	s.properties.Snapshot = true
@@ -191,27 +193,32 @@
 		}
 
 		// Generate the snapshot from the member info.
-		p := s.buildSnapshot(ctx, sdkVariants)
-		zip := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), p.Base(), p)
-		s.snapshotFile = android.OptionalPathForPath(zip)
+		s.buildSnapshot(ctx, sdkVariants)
 	}
 }
 
 func (s *sdk) AndroidMkEntries() []android.AndroidMkEntries {
-	if !s.snapshotFile.Valid() {
+	if !s.snapshotFile.Valid() != !s.infoFile.Valid() {
+		panic("Snapshot (%q) and info file (%q) should both be set or neither should be set.")
+	} else if !s.snapshotFile.Valid() {
 		return []android.AndroidMkEntries{}
 	}
 
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "FAKE",
 		OutputFile: s.snapshotFile,
-		DistFiles:  android.MakeDefaultDistFiles(s.snapshotFile.Path()),
+		DistFiles:  android.MakeDefaultDistFiles(s.snapshotFile.Path(), s.infoFile.Path()),
 		Include:    "$(BUILD_PHONY_PACKAGE)",
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
 			func(w io.Writer, name, prefix, moduleDir string) {
 				// Allow the sdk to be built by simply passing its name on the command line.
 				fmt.Fprintln(w, ".PHONY:", s.Name())
 				fmt.Fprintln(w, s.Name()+":", s.snapshotFile.String())
+
+				// Allow the sdk info to be built by simply passing its name on the command line.
+				infoTarget := s.Name() + ".info"
+				fmt.Fprintln(w, ".PHONY:", infoTarget)
+				fmt.Fprintln(w, infoTarget+":", s.infoFile.String())
 			},
 		},
 	}}
@@ -274,7 +281,6 @@
 func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
 	ctx.BottomUp("SdkMember", memberMutator).Parallel()
 	ctx.TopDown("SdkMember_deps", memberDepsMutator).Parallel()
-	ctx.BottomUp("SdkMemberInterVersion", memberInterVersionMutator).Parallel()
 }
 
 type dependencyTag struct {
@@ -286,38 +292,6 @@
 
 var _ android.ExcludeFromApexContentsTag = dependencyTag{}
 
-// For dependencies from an in-development version of an SDK member to frozen versions of the same member
-// e.g. libfoo -> libfoo.mysdk.11 and libfoo.mysdk.12
-//
-// The dependency represented by this tag requires that for every APEX variant created for the
-// `from` module that an equivalent APEX variant is created for the 'to' module. This is because an
-// APEX that requires a specific version of an sdk (via the `uses_sdks` property will replace
-// dependencies on the unversioned sdk member with a dependency on the appropriate versioned sdk
-// member. In order for that to work the versioned sdk member needs to have a variant for that APEX.
-// As it is not known at the time that the APEX variants are created which specific APEX variants of
-// a versioned sdk members will be required it is necessary for the versioned sdk members to have
-// variants for any APEX that it could be used within.
-//
-// If the APEX selects a versioned sdk member then it will not have a dependency on the `from`
-// module at all so any dependencies of that module will not affect the APEX. However, if the APEX
-// selects the unversioned sdk member then it must exclude all the versioned sdk members. In no
-// situation would this dependency cause the `to` module to be added to the APEX hence why this tag
-// also excludes the `to` module from being added to the APEX contents.
-type sdkMemberVersionedDepTag struct {
-	dependencyTag
-	member  string
-	version string
-}
-
-func (t sdkMemberVersionedDepTag) AlwaysRequireApexVariant() bool {
-	return true
-}
-
-// Mark this tag so dependencies that use it are excluded from visibility enforcement.
-func (t sdkMemberVersionedDepTag) ExcludeFromVisibilityEnforcement() {}
-
-var _ android.AlwaysRequireApexVariantTag = sdkMemberVersionedDepTag{}
-
 // Step 1: create dependencies from an SDK module to its members.
 func memberMutator(mctx android.BottomUpMutatorContext) {
 	if s, ok := mctx.Module().(*sdk); ok {
@@ -376,22 +350,6 @@
 	}
 }
 
-// Step 3: create dependencies from the unversioned SDK member to snapshot versions
-// of the same member. By having these dependencies, they are mutated for multiple Mainline modules
-// (apex and apk), each of which might want different sdks to be built with. For example, if both
-// apex A and B are referencing libfoo which is a member of sdk 'mysdk', the two APEXes can be
-// built with libfoo.mysdk.11 and libfoo.mysdk.12, respectively depending on which sdk they are
-// using.
-func memberInterVersionMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() && m.IsVersioned() {
-		if !m.ContainingSdk().Unversioned() {
-			memberName := m.MemberName()
-			tag := sdkMemberVersionedDepTag{member: memberName, version: m.ContainingSdk().Version}
-			mctx.AddReverseDependency(mctx.Module(), tag, memberName)
-		}
-	}
-}
-
 // An interface that encapsulates all the functionality needed to manage the sdk dependencies.
 //
 // It is a mixture of apex and sdk module functionality.
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 40de150..1ec12c3 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -119,18 +119,6 @@
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: [
-        "//other/foo",
-        "//package",
-        "//prebuilts/mysdk",
-    ],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-java_import {
     name: "myjavalib",
     prefer: false,
     visibility: [
@@ -143,14 +131,6 @@
 }
 
 java_import {
-    name: "mysdk_mypublicjavalib@current",
-    sdk_member_name: "mypublicjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/mypublicjavalib.jar"],
-}
-
-java_import {
     name: "mypublicjavalib",
     prefer: false,
     visibility: ["//visibility:public"],
@@ -159,18 +139,6 @@
 }
 
 java_import {
-    name: "mysdk_mydefaultedjavalib@current",
-    sdk_member_name: "mydefaultedjavalib",
-    visibility: [
-        "//other/bar",
-        "//package",
-        "//prebuilts/mysdk",
-    ],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/mydefaultedjavalib.jar"],
-}
-
-java_import {
     name: "mydefaultedjavalib",
     prefer: false,
     visibility: [
@@ -183,17 +151,6 @@
 }
 
 java_import {
-    name: "mysdk_myprivatejavalib@current",
-    sdk_member_name: "myprivatejavalib",
-    visibility: [
-        "//package",
-        "//prebuilts/mysdk",
-    ],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myprivatejavalib.jar"],
-}
-
-java_import {
     name: "myprivatejavalib",
     prefer: false,
     visibility: [
@@ -203,20 +160,6 @@
     apex_available: ["//apex_available:platform"],
     jars: ["java/myprivatejavalib.jar"],
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: [
-        "//other/foo",
-        "//package:__subpackages__",
-    ],
-    java_header_libs: [
-        "mysdk_myjavalib@current",
-        "mysdk_mypublicjavalib@current",
-        "mysdk_mydefaultedjavalib@current",
-        "mysdk_myprivatejavalib@current",
-    ],
-}
 `))
 }
 
@@ -263,7 +206,10 @@
 	result := testSdkWithFs(t, sdk, nil)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`))
+		checkAllOtherCopyRules(`
+.intermediates/mysdk/common_os/mysdk-current.info -> mysdk-current.info
+.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip
+`))
 }
 
 type EmbeddedPropertiesStruct struct {
@@ -447,26 +393,12 @@
 // 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: ["java/myjavalib.jar"],
-}
-
-java_import {
     name: "myjavalib",
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     jars: ["java/myjavalib.jar"],
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
 			`),
 		)
 	})
@@ -486,26 +418,12 @@
 // 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: ["java/myjavalib.jar"],
-}
-
-java_import {
     name: "myjavalib",
     prefer: true,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     jars: ["java/myjavalib.jar"],
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
 			`),
 		)
 	})
@@ -525,14 +443,6 @@
 // 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: ["java/myjavalib.jar"],
-}
-
-java_import {
     name: "myjavalib",
     prefer: false,
     use_source_config_var: {
@@ -543,113 +453,10 @@
     apex_available: ["//apex_available:platform"],
     jars: ["java/myjavalib.jar"],
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
 			`),
 		)
 	})
 
-	t.Run("SOONG_SDK_SNAPSHOT_VERSION=unversioned", func(t *testing.T) {
-		result := android.GroupFixturePreparers(
-			preparer,
-			android.FixtureMergeEnv(map[string]string{
-				"SOONG_SDK_SNAPSHOT_VERSION": "unversioned",
-			}),
-		).RunTest(t)
-
-		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk.zip")
-
-		CheckSnapshot(t, result, "mysdk", "",
-			checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-			`),
-		)
-	})
-
-	t.Run("SOONG_SDK_SNAPSHOT_VERSION=current", func(t *testing.T) {
-		result := android.GroupFixturePreparers(
-			preparer,
-			android.FixtureMergeEnv(map[string]string{
-				"SOONG_SDK_SNAPSHOT_VERSION": "current",
-			}),
-		).RunTest(t)
-
-		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
-
-		CheckSnapshot(t, result, "mysdk", "",
-			checkAndroidBpContents(`
-// 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: ["java/myjavalib.jar"],
-}
-
-java_import {
-    name: "myjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
-			`),
-		)
-	})
-
-	t.Run("SOONG_SDK_SNAPSHOT_VERSION=2", func(t *testing.T) {
-		result := android.GroupFixturePreparers(
-			preparer,
-			android.FixtureMergeEnv(map[string]string{
-				"SOONG_SDK_SNAPSHOT_VERSION": "2",
-			}),
-		).RunTest(t)
-
-		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-2.zip")
-
-		CheckSnapshot(t, result, "mysdk", "",
-			checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_myjavalib@2",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-sdk_snapshot {
-    name: "mysdk@2",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@2"],
-}
-			`),
-			// A versioned snapshot cannot be used on its own so add the source back in.
-			snapshotTestPreparer(checkSnapshotWithoutSource, android.FixtureWithRootAndroidBp(bp)),
-		)
-	})
-
 	t.Run("SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE=S", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
 			prepareForSdkTestWithJava,
@@ -666,6 +473,9 @@
 				name: "mybootclasspathfragment",
 				apex_available: ["myapex"],
 				contents: ["mysdklibrary"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_sdk_library {
@@ -682,7 +492,7 @@
 		).RunTest(t)
 
 		CheckSnapshot(t, result, "mysdk", "",
-			checkUnversionedAndroidBpContents(`
+			checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_bootclasspath_fragment {
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index 819083d..1ac405d 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -22,7 +22,7 @@
 	"android/soong/java"
 )
 
-func testSnapshotWithSystemServerClasspathFragment(t *testing.T, targetBuildRelease string, expectedUnversionedSdkSnapshot string, expectedVersionedSdkSnapshot string) {
+func testSnapshotWithSystemServerClasspathFragment(t *testing.T, sdk string, targetBuildRelease string, expectedSdkSnapshot string) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithJavaDefaultModules,
@@ -30,23 +30,13 @@
 		java.FixtureWithLastReleaseApis("mysdklibrary"),
 		dexpreopt.FixtureSetApexSystemServerJars("myapex:mylib", "myapex:mysdklibrary"),
 		android.FixtureModifyEnv(func(env map[string]string) {
-			env["SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE"] = targetBuildRelease
+			if targetBuildRelease != "latest" {
+				env["SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE"] = targetBuildRelease
+			}
 		}),
 		prepareForSdkTestWithApex,
 
-		android.FixtureWithRootAndroidBp(`
-			sdk {
-				name: "mysdk",
-				systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
-				java_sdk_libs: [
-					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
-					// a java_sdk_libs module because it is used in the mysystemserverclasspathfragment's
-					// contents property. However, it is specified here to ensure that duplicates are
-					// correctly deduped.
-					"mysdklibrary",
-				],
-			}
-
+		android.FixtureWithRootAndroidBp(sdk+`
 			apex {
 				name: "myapex",
 				key: "myapex.key",
@@ -86,58 +76,27 @@
 	).RunTest(t)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(expectedUnversionedSdkSnapshot),
-		checkVersionedAndroidBpContents(expectedVersionedSdkSnapshot),
+		checkAndroidBpContents(expectedSdkSnapshot),
 	)
 }
 
 func TestSnapshotWithSystemServerClasspathFragment(t *testing.T) {
-	t.Run("target-s", func(t *testing.T) {
-		testSnapshotWithSystemServerClasspathFragment(t, "S", `
-// This is auto-generated. DO NOT EDIT.
 
-java_sdk_library_import {
-    name: "mysdklibrary",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    shared_library: false,
-    public: {
-        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
-        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
-        current_api: "sdk_library/public/mysdklibrary.txt",
-        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
-        sdk_version: "current",
-    },
+	commonSdk := `
+sdk {
+	name: "mysdk",
+	systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
+	java_sdk_libs: [
+		// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+		// a java_sdk_libs module because it is used in the mysystemserverclasspathfragment's
+		// contents property. However, it is specified here to ensure that duplicates are
+		// correctly deduped.
+		"mysdklibrary",
+	],
 }
-`, `
-// This is auto-generated. DO NOT EDIT.
+	`
 
-java_sdk_library_import {
-    name: "mysdk_mysdklibrary@current",
-    sdk_member_name: "mysdklibrary",
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    shared_library: false,
-    public: {
-        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
-        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
-        current_api: "sdk_library/public/mysdklibrary.txt",
-        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
-        sdk_version: "current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_mysdklibrary@current"],
-}
-`)
-	})
-
-	t.Run("target-t", func(t *testing.T) {
-		testSnapshotWithSystemServerClasspathFragment(t, "Tiramisu", `
+	expectedLatestSnapshot := `
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -174,12 +133,36 @@
         "mysdklibrary",
     ],
 }
-`, `
+`
+
+	t.Run("target-s", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "S", `
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "mysdk_mysdklibrary@current",
-    sdk_member_name: "mysdklibrary",
+    name: "mysdklibrary",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    shared_library: false,
+    public: {
+        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
+        current_api: "sdk_library/public/mysdklibrary.txt",
+        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
+        sdk_version: "current",
+    },
+}
+`)
+	})
+
+	t.Run("target-t", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "Tiramisu", `
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "mysdklibrary",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
     shared_library: false,
@@ -193,8 +176,8 @@
 }
 
 java_import {
-    name: "mysdk_mylib@current",
-    sdk_member_name: "mylib",
+    name: "mylib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
     jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
@@ -202,23 +185,28 @@
 }
 
 prebuilt_systemserverclasspath_fragment {
-    name: "mysdk_mysystemserverclasspathfragment@current",
-    sdk_member_name: "mysystemserverclasspathfragment",
+    name: "mysystemserverclasspathfragment",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
     contents: [
-        "mysdk_mylib@current",
-        "mysdk_mysdklibrary@current",
+        "mylib",
+        "mysdklibrary",
     ],
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_mysdklibrary@current"],
-    java_systemserver_libs: ["mysdk_mylib@current"],
-    systemserverclasspath_fragments: ["mysdk_mysystemserverclasspathfragment@current"],
-}
 `)
 	})
+
+	t.Run("added-directly", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, `latest`, expectedLatestSnapshot)
+	})
+
+	t.Run("added-via-apex", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, `
+			sdk {
+				name: "mysdk",
+				apexes: ["myapex"],
+			}
+		`, `latest`, expectedLatestSnapshot)
+	})
 }
diff --git a/sdk/testing.go b/sdk/testing.go
index 062f200..bed11b2 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -122,28 +122,18 @@
 	}
 }
 
-func pathsToStrings(paths android.Paths) []string {
-	var ret []string
-	for _, p := range paths {
-		ret = append(ret, p.String())
-	}
-	return ret
-}
-
 // Analyse the sdk build rules to extract information about what it is doing.
 //
 // e.g. find the src/dest pairs from each cp command, the various zip files
 // generated, etc.
 func getSdkSnapshotBuildInfo(t *testing.T, result *android.TestResult, sdk *sdk) *snapshotBuildInfo {
 	info := &snapshotBuildInfo{
-		t:                            t,
-		r:                            result,
-		version:                      sdk.builderForTests.version,
-		androidBpContents:            sdk.GetAndroidBpContentsForTests(),
-		androidUnversionedBpContents: sdk.GetUnversionedAndroidBpContentsForTests(),
-		androidVersionedBpContents:   sdk.GetVersionedAndroidBpContentsForTests(),
-		snapshotTestCustomizations:   map[snapshotTest]*snapshotTestCustomization{},
-		targetBuildRelease:           sdk.builderForTests.targetBuildRelease,
+		t:                          t,
+		r:                          result,
+		androidBpContents:          sdk.GetAndroidBpContentsForTests(),
+		infoContents:               sdk.GetInfoContentsForTests(),
+		snapshotTestCustomizations: map[snapshotTest]*snapshotTestCustomization{},
+		targetBuildRelease:         sdk.builderForTests.targetBuildRelease,
 	}
 
 	buildParams := sdk.BuildParamsForTests()
@@ -257,10 +247,7 @@
 	if dir != "" {
 		dir = filepath.Clean(dir) + "/"
 	}
-	suffix := ""
-	if snapshotBuildInfo.version != soongSdkSnapshotVersionUnversioned {
-		suffix = "-" + snapshotBuildInfo.version
-	}
+	suffix := "-" + soongSdkSnapshotVersionCurrent
 
 	expectedZipPath := fmt.Sprintf(".intermediates/%s%s/%s/%s%s.zip", dir, name, variant, name, suffix)
 	android.AssertStringEquals(t, "Snapshot zip file in wrong place", expectedZipPath, actual)
@@ -344,33 +331,6 @@
 	}
 }
 
-// Check that the snapshot's unversioned generated Android.bp is correct.
-//
-// This func should be used to check the general snapshot generation code.
-//
-// Both the expected and actual string are both trimmed before comparing.
-func checkUnversionedAndroidBpContents(expected string) snapshotBuildInfoChecker {
-	return func(info *snapshotBuildInfo) {
-		info.t.Helper()
-		android.AssertTrimmedStringEquals(info.t, "unversioned Android.bp contents do not match", expected, info.androidUnversionedBpContents)
-	}
-}
-
-// Check that the snapshot's versioned generated Android.bp is correct.
-//
-// This func should only be used to check the version specific snapshot generation code,
-// i.e. the encoding of version into module names and the generation of the _snapshot module. The
-// general snapshot generation code should be checked using the checkUnversionedAndroidBpContents()
-// func.
-//
-// Both the expected and actual string are both trimmed before comparing.
-func checkVersionedAndroidBpContents(expected string) snapshotBuildInfoChecker {
-	return func(info *snapshotBuildInfo) {
-		info.t.Helper()
-		android.AssertTrimmedStringEquals(info.t, "versioned Android.bp contents do not match", expected, info.androidVersionedBpContents)
-	}
-}
-
 // Check that the snapshot's copy rules are correct.
 //
 // The copy rules are formatted as <src> -> <dest>, one per line and then compared
@@ -402,6 +362,17 @@
 	}
 }
 
+// Check that the snapshot's info contents are ciorrect.
+//
+// Both the expected and actual string are both trimmed before comparing.
+func checkInfoContents(config android.Config, expected string) snapshotBuildInfoChecker {
+	return func(info *snapshotBuildInfo) {
+		info.t.Helper()
+		android.AssertTrimmedStringEquals(info.t, "info contents do not match",
+			expected, android.StringRelativeToTop(config, info.infoContents))
+	}
+}
+
 type resultChecker func(t *testing.T, result *android.TestResult)
 
 // snapshotTestPreparer registers a preparer that will be used to customize the specified
@@ -465,19 +436,11 @@
 	// The result from RunTest()
 	r *android.TestResult
 
-	// The version of the generated snapshot.
-	//
-	// See snapshotBuilder.version for more information about this field.
-	version string
-
 	// The contents of the generated Android.bp file
 	androidBpContents string
 
-	// The contents of the unversioned Android.bp file
-	androidUnversionedBpContents string
-
-	// The contents of the versioned Android.bp file
-	androidVersionedBpContents string
+	// The contents of the info file.
+	infoContents string
 
 	// The paths, relative to the snapshot root, of all files and directories copied into the
 	// snapshot.
diff --git a/sdk/update.go b/sdk/update.go
index 0178874..995da74 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -15,6 +15,8 @@
 package sdk
 
 import (
+	"bytes"
+	"encoding/json"
 	"fmt"
 	"reflect"
 	"sort"
@@ -33,7 +35,7 @@
 // ========================================================
 //
 // SOONG_SDK_SNAPSHOT_PREFER
-//     By default every unversioned module in the generated snapshot has prefer: false. Building it
+//     By default every module in the generated snapshot has prefer: false. Building it
 //     with SOONG_SDK_SNAPSHOT_PREFER=true will force them to use prefer: true.
 //
 // SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR
@@ -67,20 +69,6 @@
 //     maintainable solution has been implemented.
 //     TODO(b/174997203): Remove when no longer necessary.
 //
-// SOONG_SDK_SNAPSHOT_VERSION
-//     This provides control over the version of the generated snapshot.
-//
-//     SOONG_SDK_SNAPSHOT_VERSION=current will generate unversioned and versioned prebuilts and a
-//     versioned snapshot module. This is the default behavior. The zip file containing the
-//     generated snapshot will be <sdk-name>-current.zip.
-//
-//     SOONG_SDK_SNAPSHOT_VERSION=unversioned will generate unversioned prebuilts only and the zip
-//     file containing the generated snapshot will be <sdk-name>.zip.
-//
-//     SOONG_SDK_SNAPSHOT_VERSION=<number> will generate versioned prebuilts and a versioned
-//     snapshot module only. The zip file containing the generated snapshot will be
-//     <sdk-name>-<number>.zip.
-//
 // SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE
 //     This allows the target build release (i.e. the release version of the build within which
 //     the snapshot will be used) of the snapshot to be specified. If unspecified then it defaults
@@ -128,8 +116,7 @@
 )
 
 const (
-	soongSdkSnapshotVersionUnversioned = "unversioned"
-	soongSdkSnapshotVersionCurrent     = "current"
+	soongSdkSnapshotVersionCurrent = "current"
 )
 
 type generatedContents struct {
@@ -161,13 +148,13 @@
 // IndentedPrintf will add spaces to indent the line to the appropriate level before printing the
 // arguments.
 func (gc *generatedContents) IndentedPrintf(format string, args ...interface{}) {
-	fmt.Fprintf(&(gc.content), strings.Repeat("    ", gc.indentLevel)+format, args...)
+	_, _ = fmt.Fprintf(&(gc.content), strings.Repeat("    ", gc.indentLevel)+format, args...)
 }
 
 // UnindentedPrintf does not add spaces to indent the line to the appropriate level before printing
 // the arguments.
 func (gc *generatedContents) UnindentedPrintf(format string, args ...interface{}) {
-	fmt.Fprintf(&(gc.content), format, args...)
+	_, _ = fmt.Fprintf(&(gc.content), format, args...)
 }
 
 func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
@@ -219,9 +206,19 @@
 				exportedComponentsInfo = ctx.OtherModuleProvider(child, android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
 			}
 
+			var container android.SdkAware
+			if parent != ctx.Module() {
+				container = parent.(android.SdkAware)
+			}
+
 			export := memberTag.ExportMember()
 			s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{
-				s, memberType, child.(android.SdkAware), export, exportedComponentsInfo,
+				sdkVariant:             s,
+				memberType:             memberType,
+				variant:                child.(android.SdkAware),
+				container:              container,
+				export:                 export,
+				exportedComponentsInfo: exportedComponentsInfo,
 			})
 
 			// Recurse down into the member's dependencies as it may have dependencies that need to be
@@ -256,13 +253,19 @@
 			member = &sdkMember{memberType: memberType, name: name}
 			byName[name] = member
 			byType[memberType] = append(byType[memberType], member)
+		} else if member.memberType != memberType {
+			// validate whether this is the same member type or and overriding member type
+			if memberType.Overrides(member.memberType) {
+				member.memberType = memberType
+			} else if !member.memberType.Overrides(memberType) {
+				ctx.ModuleErrorf("Incompatible member types %q %q", member.memberType, memberType)
+			}
 		}
 
 		// Only append new variants to the list. This is needed because a member can be both
 		// exported by the sdk and also be a transitive sdk member.
 		member.variants = appendUniqueVariants(member.variants, variant)
 	}
-
 	var members []*sdkMember
 	for _, memberListProperty := range s.memberTypeListProperties() {
 		memberType := memberListProperty.memberType
@@ -329,15 +332,9 @@
 //         <arch>/lib/
 //            libFoo.so   : a stub library
 
-// A name that uniquely identifies a prebuilt SDK member for a version of SDK snapshot
-// This isn't visible to users, so could be changed in future.
-func versionedSdkMemberName(ctx android.ModuleContext, memberName string, version string) string {
-	return ctx.ModuleName() + "_" + memberName + string(android.SdkVersionSeparator) + version
-}
-
 // buildSnapshot is the main function in this source file. It creates rules to copy
 // the contents (header files, stub libraries, etc) into the zip file.
-func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) android.OutputPath {
+func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) {
 
 	// Aggregate all the sdkMemberVariantDep instances from all the sdk variants.
 	hasLicenses := false
@@ -386,20 +383,9 @@
 	}
 
 	config := ctx.Config()
-	version := config.GetenvWithDefault("SOONG_SDK_SNAPSHOT_VERSION", "current")
 
-	// Generate versioned modules in the snapshot unless an unversioned snapshot has been requested.
-	generateVersioned := version != soongSdkSnapshotVersionUnversioned
-
-	// Generate unversioned modules in the snapshot unless a numbered snapshot has been requested.
-	//
-	// Unversioned modules are not required in that case because the numbered version will be a
-	// finalized version of the snapshot that is intended to be kept separate from the
-	generateUnversioned := version == soongSdkSnapshotVersionUnversioned || version == soongSdkSnapshotVersionCurrent
-	snapshotZipFileSuffix := ""
-	if generateVersioned {
-		snapshotZipFileSuffix = "-" + version
-	}
+	// Always add -current to the end
+	snapshotFileSuffix := "-current"
 
 	currentBuildRelease := latestBuildRelease()
 	targetBuildReleaseEnv := config.GetenvWithDefault("SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE", currentBuildRelease.name)
@@ -412,7 +398,6 @@
 	builder := &snapshotBuilder{
 		ctx:                   ctx,
 		sdk:                   s,
-		version:               version,
 		snapshotDir:           snapshotDir.OutputPath,
 		copies:                make(map[string]string),
 		filesToZip:            []android.Path{bp.path},
@@ -448,6 +433,9 @@
 	traits := s.gatherTraits()
 	for _, member := range members {
 		memberType := member.memberType
+		if !memberType.ArePrebuiltsRequired() {
+			continue
+		}
 
 		name := member.name
 		requiredTraits := traits[name]
@@ -462,38 +450,19 @@
 		s.createMemberSnapshot(memberCtx, member, prebuiltModule.(*bpModule))
 	}
 
-	// Create a transformer that will transform an unversioned module into a versioned module.
-	unversionedToVersionedTransformer := unversionedToVersionedTransformation{builder: builder}
-
-	// Create a transformer that will transform an unversioned module by replacing any references
+	// Create a transformer that will transform a module by replacing any references
 	// to internal members with a unique module name and setting prefer: false.
-	unversionedTransformer := unversionedTransformation{
+	snapshotTransformer := snapshotTransformation{
 		builder: builder,
 	}
 
-	for _, unversioned := range builder.prebuiltOrder {
+	for _, module := range builder.prebuiltOrder {
 		// Prune any empty property sets.
-		unversioned = unversioned.transform(pruneEmptySetTransformer{})
+		module = module.transform(pruneEmptySetTransformer{})
 
-		if generateVersioned {
-			// Copy the unversioned module so it can be modified to make it versioned.
-			versioned := unversioned.deepCopy()
-
-			// Transform the unversioned module into a versioned one.
-			versioned.transform(unversionedToVersionedTransformer)
-			bpFile.AddModule(versioned)
-		}
-
-		if generateUnversioned {
-			// Transform the unversioned module to make it suitable for use in the snapshot.
-			unversioned.transform(unversionedTransformer)
-			bpFile.AddModule(unversioned)
-		}
-	}
-
-	if generateVersioned {
-		// Add the sdk/module_exports_snapshot module to the bp file.
-		s.addSnapshotModule(ctx, builder, sdkVariants, memberVariantDeps)
+		// Transform the module module to make it suitable for use in the snapshot.
+		module.transform(snapshotTransformer)
+		bpFile.AddModule(module)
 	}
 
 	// generate Android.bp
@@ -515,7 +484,7 @@
 	filesToZip := builder.filesToZip
 
 	// zip them all
-	zipPath := fmt.Sprintf("%s%s.zip", ctx.ModuleName(), snapshotZipFileSuffix)
+	zipPath := fmt.Sprintf("%s%s.zip", ctx.ModuleName(), snapshotFileSuffix)
 	outputZipFile := android.PathForModuleOut(ctx, zipPath).OutputPath
 	outputDesc := "Building snapshot for " + ctx.ModuleName()
 
@@ -528,7 +497,7 @@
 		zipFile = outputZipFile
 		desc = outputDesc
 	} else {
-		intermediatePath := fmt.Sprintf("%s%s.unmerged.zip", ctx.ModuleName(), snapshotZipFileSuffix)
+		intermediatePath := fmt.Sprintf("%s%s.unmerged.zip", ctx.ModuleName(), snapshotFileSuffix)
 		zipFile = android.PathForModuleOut(ctx, intermediatePath).OutputPath
 		desc = "Building intermediate snapshot for " + ctx.ModuleName()
 	}
@@ -553,7 +522,125 @@
 		})
 	}
 
-	return outputZipFile
+	modules := s.generateInfoData(ctx, memberVariantDeps)
+
+	// Output the modules information as pretty printed JSON.
+	info := newGeneratedFile(ctx, fmt.Sprintf("%s%s.info", ctx.ModuleName(), snapshotFileSuffix))
+	output, err := json.MarshalIndent(modules, "", "  ")
+	if err != nil {
+		ctx.ModuleErrorf("error generating %q: %s", info, err)
+	}
+	builder.infoContents = string(output)
+	info.generatedContents.UnindentedPrintf("%s", output)
+	info.build(pctx, ctx, nil)
+	infoPath := info.path
+	installedInfo := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), infoPath.Base(), infoPath)
+	s.infoFile = android.OptionalPathForPath(installedInfo)
+
+	// Install the zip, making sure that the info file has been installed as well.
+	installedZip := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), outputZipFile.Base(), outputZipFile, installedInfo)
+	s.snapshotFile = android.OptionalPathForPath(installedZip)
+}
+
+type moduleInfo struct {
+	// The type of the module, e.g. java_sdk_library
+	moduleType string
+	// The name of the module.
+	name string
+	// A list of additional dependencies of the module.
+	deps []string
+	// Additional member specific properties.
+	// These will be added into the generated JSON alongside the above properties.
+	memberSpecific map[string]interface{}
+}
+
+func (m *moduleInfo) MarshalJSON() ([]byte, error) {
+	buffer := bytes.Buffer{}
+
+	separator := ""
+	writeObjectPair := func(key string, value interface{}) {
+		buffer.WriteString(fmt.Sprintf("%s%q: ", separator, key))
+		b, err := json.Marshal(value)
+		if err != nil {
+			panic(err)
+		}
+		buffer.Write(b)
+		separator = ","
+	}
+
+	buffer.WriteString("{")
+	writeObjectPair("@type", m.moduleType)
+	writeObjectPair("@name", m.name)
+	if m.deps != nil {
+		writeObjectPair("@deps", m.deps)
+	}
+	for _, k := range android.SortedStringKeys(m.memberSpecific) {
+		v := m.memberSpecific[k]
+		writeObjectPair(k, v)
+	}
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+var _ json.Marshaler = (*moduleInfo)(nil)
+
+// generateInfoData creates a list of moduleInfo structures that will be marshalled into JSON.
+func (s *sdk) generateInfoData(ctx android.ModuleContext, memberVariantDeps []sdkMemberVariantDep) interface{} {
+	modules := []*moduleInfo{}
+	sdkInfo := moduleInfo{
+		moduleType:     "sdk",
+		name:           ctx.ModuleName(),
+		memberSpecific: map[string]interface{}{},
+	}
+	modules = append(modules, &sdkInfo)
+
+	name2Info := map[string]*moduleInfo{}
+	getModuleInfo := func(module android.Module) *moduleInfo {
+		name := module.Name()
+		info := name2Info[name]
+		if info == nil {
+			moduleType := ctx.OtherModuleType(module)
+			// Remove any suffix added when creating modules dynamically.
+			moduleType = strings.Split(moduleType, "__")[0]
+			info = &moduleInfo{
+				moduleType: moduleType,
+				name:       name,
+			}
+
+			additionalSdkInfo := ctx.OtherModuleProvider(module, android.AdditionalSdkInfoProvider).(android.AdditionalSdkInfo)
+			info.memberSpecific = additionalSdkInfo.Properties
+
+			name2Info[name] = info
+		}
+		return info
+	}
+
+	for _, memberVariantDep := range memberVariantDeps {
+		propertyName := memberVariantDep.memberType.SdkPropertyName()
+		var list []string
+		if v, ok := sdkInfo.memberSpecific[propertyName]; ok {
+			list = v.([]string)
+		}
+
+		memberName := memberVariantDep.variant.Name()
+		list = append(list, memberName)
+		sdkInfo.memberSpecific[propertyName] = android.SortedUniqueStrings(list)
+
+		if memberVariantDep.container != nil {
+			containerInfo := getModuleInfo(memberVariantDep.container)
+			containerInfo.deps = android.SortedUniqueStrings(append(containerInfo.deps, memberName))
+		}
+
+		// Make sure that the module info is created for each module.
+		getModuleInfo(memberVariantDep.variant)
+	}
+
+	for _, memberName := range android.SortedStringKeys(name2Info) {
+		info := name2Info[memberName]
+		modules = append(modules, info)
+	}
+
+	return modules
 }
 
 // filterOutComponents removes any item from the deps list that is a component of another item in
@@ -562,7 +649,7 @@
 func filterOutComponents(ctx android.ModuleContext, deps []sdkMemberVariantDep) []sdkMemberVariantDep {
 	// Collate the set of components that all the modules added to the sdk provide.
 	components := map[string]*sdkMemberVariantDep{}
-	for i, _ := range deps {
+	for i := range deps {
 		dep := &deps[i]
 		for _, c := range dep.exportedComponentsInfo.Components {
 			components[c] = dep
@@ -597,81 +684,6 @@
 	return filtered
 }
 
-// addSnapshotModule adds the sdk_snapshot/module_exports_snapshot module to the builder.
-func (s *sdk) addSnapshotModule(ctx android.ModuleContext, builder *snapshotBuilder, sdkVariants []*sdk, memberVariantDeps []sdkMemberVariantDep) {
-	bpFile := builder.bpFile
-
-	snapshotName := ctx.ModuleName() + string(android.SdkVersionSeparator) + builder.version
-	var snapshotModuleType string
-	if s.properties.Module_exports {
-		snapshotModuleType = "module_exports_snapshot"
-	} else {
-		snapshotModuleType = "sdk_snapshot"
-	}
-	snapshotModule := bpFile.newModule(snapshotModuleType)
-	snapshotModule.AddProperty("name", snapshotName)
-
-	// Make sure that the snapshot has the same visibility as the sdk.
-	visibility := android.EffectiveVisibilityRules(ctx, s).Strings()
-	if len(visibility) != 0 {
-		snapshotModule.AddProperty("visibility", visibility)
-	}
-
-	addHostDeviceSupportedProperties(s.ModuleBase.DeviceSupported(), s.ModuleBase.HostSupported(), snapshotModule)
-
-	combinedPropertiesList := s.collateSnapshotModuleInfo(ctx, sdkVariants, memberVariantDeps)
-	commonCombinedProperties := s.optimizeSnapshotModuleProperties(ctx, combinedPropertiesList)
-
-	s.addSnapshotPropertiesToPropertySet(builder, snapshotModule, commonCombinedProperties)
-
-	targetPropertySet := snapshotModule.AddPropertySet("target")
-
-	// Create a mapping from osType to combined properties.
-	osTypeToCombinedProperties := map[android.OsType]*combinedSnapshotModuleProperties{}
-	for _, combined := range combinedPropertiesList {
-		osTypeToCombinedProperties[combined.sdkVariant.Os()] = combined
-	}
-
-	// Iterate over the os types in a fixed order.
-	for _, osType := range s.getPossibleOsTypes() {
-		if combined, ok := osTypeToCombinedProperties[osType]; ok {
-			osPropertySet := targetPropertySet.AddPropertySet(osType.Name)
-
-			s.addSnapshotPropertiesToPropertySet(builder, osPropertySet, combined)
-		}
-	}
-
-	// If host is supported and any member is host OS dependent then disable host
-	// by default, so that we can enable each host OS variant explicitly. This
-	// avoids problems with implicitly enabled OS variants when the snapshot is
-	// used, which might be different from this run (e.g. different build OS).
-	if s.HostSupported() {
-		var supportedHostTargets []string
-		for _, memberVariantDep := range memberVariantDeps {
-			if memberVariantDep.memberType.IsHostOsDependent() && memberVariantDep.variant.Target().Os.Class == android.Host {
-				targetString := memberVariantDep.variant.Target().Os.String() + "_" + memberVariantDep.variant.Target().Arch.ArchType.String()
-				if !android.InList(targetString, supportedHostTargets) {
-					supportedHostTargets = append(supportedHostTargets, targetString)
-				}
-			}
-		}
-		if len(supportedHostTargets) > 0 {
-			hostPropertySet := targetPropertySet.AddPropertySet("host")
-			hostPropertySet.AddProperty("enabled", false)
-		}
-		// Enable the <os>_<arch> variant explicitly when we've disabled it by default on host.
-		for _, hostTarget := range supportedHostTargets {
-			propertySet := targetPropertySet.AddPropertySet(hostTarget)
-			propertySet.AddProperty("enabled", true)
-		}
-	}
-
-	// Prune any empty property sets.
-	snapshotModule.transform(pruneEmptySetTransformer{})
-
-	bpFile.AddModule(snapshotModule)
-}
-
 // Check the syntax of the generated Android.bp file contents and if they are
 // invalid then log an error with the contents (tagged with line numbers) and the
 // errors that were found so that it is easy to see where the problem lies.
@@ -808,95 +820,34 @@
 	}
 }
 
-func (s *sdk) addSnapshotPropertiesToPropertySet(builder *snapshotBuilder, propertySet android.BpPropertySet, combined *combinedSnapshotModuleProperties) {
-	staticProperties := combined.staticProperties
-	multilib := staticProperties.Compile_multilib
-	if multilib != "" && multilib != "both" {
-		// Compile_multilib defaults to both so only needs to be set when it's specified and not both.
-		propertySet.AddProperty("compile_multilib", multilib)
-	}
-
-	dynamicMemberTypeListProperties := combined.dynamicProperties
-	for _, memberListProperty := range s.memberTypeListProperties() {
-		if memberListProperty.getter == nil {
-			continue
-		}
-		if !isMemberTypeSupportedByTargetBuildRelease(memberListProperty.memberType, builder.targetBuildRelease) {
-			continue
-		}
-		names := memberListProperty.getter(dynamicMemberTypeListProperties)
-		if len(names) > 0 {
-			propertySet.AddProperty(memberListProperty.propertyName(), builder.versionedSdkMemberNames(names, false))
-		}
-	}
-}
-
 type propertyTag struct {
 	name string
 }
 
 var _ android.BpPropertyTag = propertyTag{}
 
-// A BpPropertyTag to add to a property that contains references to other sdk members.
+// BpPropertyTag instances to add to a property that contains references to other sdk members.
 //
-// This will cause the references to be rewritten to a versioned reference in the version
-// specific instance of a snapshot module.
+// These will ensure that the referenced modules are available, if required.
 var requiredSdkMemberReferencePropertyTag = propertyTag{"requiredSdkMemberReferencePropertyTag"}
 var optionalSdkMemberReferencePropertyTag = propertyTag{"optionalSdkMemberReferencePropertyTag"}
 
-// A BpPropertyTag that indicates the property should only be present in the versioned
-// module.
-//
-// This will cause the property to be removed from the unversioned instance of a
-// snapshot module.
-var sdkVersionedOnlyPropertyTag = propertyTag{"sdkVersionedOnlyPropertyTag"}
-
-type unversionedToVersionedTransformation struct {
+type snapshotTransformation struct {
 	identityTransformation
 	builder *snapshotBuilder
 }
 
-func (t unversionedToVersionedTransformation) transformModule(module *bpModule) *bpModule {
-	// Use a versioned name for the module but remember the original name for the
-	// snapshot.
-	name := module.Name()
-	module.setProperty("name", t.builder.versionedSdkMemberName(name, true))
-	module.insertAfter("name", "sdk_member_name", name)
-	// Remove the prefer property if present as versioned modules never need marking with prefer.
-	module.removeProperty("prefer")
-	// Ditto for use_source_config_var
-	module.removeProperty("use_source_config_var")
-	return module
-}
-
-func (t unversionedToVersionedTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
-	if tag == requiredSdkMemberReferencePropertyTag || tag == optionalSdkMemberReferencePropertyTag {
-		required := tag == requiredSdkMemberReferencePropertyTag
-		return t.builder.versionedSdkMemberNames(value.([]string), required), tag
-	} else {
-		return value, tag
-	}
-}
-
-type unversionedTransformation struct {
-	identityTransformation
-	builder *snapshotBuilder
-}
-
-func (t unversionedTransformation) transformModule(module *bpModule) *bpModule {
+func (t snapshotTransformation) transformModule(module *bpModule) *bpModule {
 	// If the module is an internal member then use a unique name for it.
 	name := module.Name()
-	module.setProperty("name", t.builder.unversionedSdkMemberName(name, true))
+	module.setProperty("name", t.builder.snapshotSdkMemberName(name, true))
 	return module
 }
 
-func (t unversionedTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+func (t snapshotTransformation) transformProperty(_ string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
 	if tag == requiredSdkMemberReferencePropertyTag || tag == optionalSdkMemberReferencePropertyTag {
 		required := tag == requiredSdkMemberReferencePropertyTag
-		return t.builder.unversionedSdkMemberNames(value.([]string), required), tag
-	} else if tag == sdkVersionedOnlyPropertyTag {
-		// The property is not allowed in the unversioned module so remove it.
-		return nil, nil
+		return t.builder.snapshotSdkMemberNames(value.([]string), required), tag
 	} else {
 		return value, tag
 	}
@@ -908,7 +859,7 @@
 
 var _ bpTransformer = (*pruneEmptySetTransformer)(nil)
 
-func (t pruneEmptySetTransformer) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t pruneEmptySetTransformer) transformPropertySetAfterContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
 	if len(propertySet.properties) == 0 {
 		return nil, nil
 	} else {
@@ -917,20 +868,12 @@
 }
 
 func generateBpContents(contents *generatedContents, bpFile *bpFile) {
-	generateFilteredBpContents(contents, bpFile, func(*bpModule) bool {
-		return true
-	})
-}
-
-func generateFilteredBpContents(contents *generatedContents, bpFile *bpFile, moduleFilter func(module *bpModule) bool) {
 	contents.IndentedPrintf("// This is auto-generated. DO NOT EDIT.\n")
 	for _, bpModule := range bpFile.order {
-		if moduleFilter(bpModule) {
-			contents.IndentedPrintf("\n")
-			contents.IndentedPrintf("%s {\n", bpModule.moduleType)
-			outputPropertySet(contents, bpModule.bpPropertySet)
-			contents.IndentedPrintf("}\n")
-		}
+		contents.IndentedPrintf("\n")
+		contents.IndentedPrintf("%s {\n", bpModule.moduleType)
+		outputPropertySet(contents, bpModule.bpPropertySet)
+		contents.IndentedPrintf("}\n")
 	}
 }
 
@@ -1062,36 +1005,14 @@
 	return contents.content.String()
 }
 
-func (s *sdk) GetUnversionedAndroidBpContentsForTests() string {
-	contents := &generatedContents{}
-	generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool {
-		name := module.Name()
-		// Include modules that are either unversioned or have no name.
-		return !strings.Contains(name, "@")
-	})
-	return contents.content.String()
-}
-
-func (s *sdk) GetVersionedAndroidBpContentsForTests() string {
-	contents := &generatedContents{}
-	generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool {
-		name := module.Name()
-		// Include modules that are either versioned or have no name.
-		return name == "" || strings.Contains(name, "@")
-	})
-	return contents.content.String()
+func (s *sdk) GetInfoContentsForTests() string {
+	return s.builderForTests.infoContents
 }
 
 type snapshotBuilder struct {
 	ctx android.ModuleContext
 	sdk *sdk
 
-	// The version of the generated snapshot.
-	//
-	// See the documentation of SOONG_SDK_SNAPSHOT_VERSION above for details of the valid values of
-	// this field.
-	version string
-
 	snapshotDir android.OutputPath
 	bpFile      *bpFile
 
@@ -1116,6 +1037,9 @@
 
 	// The target build release for which the snapshot is to be generated.
 	targetBuildRelease *buildRelease
+
+	// The contents of the .info file that describes the sdk contents.
+	infoContents string
 }
 
 func (s *snapshotBuilder) CopyToSnapshot(src android.Path, dest string) {
@@ -1242,13 +1166,6 @@
 
 	addHostDeviceSupportedProperties(deviceSupported, hostSupported, m)
 
-	// Disable installation in the versioned module of those modules that are ever installable.
-	if installable, ok := variant.(interface{ EverInstallable() bool }); ok {
-		if installable.EverInstallable() {
-			m.AddPropertyWithTag("installable", false, sdkVersionedOnlyPropertyTag)
-		}
-	}
-
 	s.prebuiltModules[name] = m
 	s.prebuiltOrder = append(s.prebuiltOrder, m)
 	return m
@@ -1281,45 +1198,28 @@
 	return optionalSdkMemberReferencePropertyTag
 }
 
-// Get a versioned name appropriate for the SDK snapshot version being taken.
-func (s *snapshotBuilder) versionedSdkMemberName(unversionedName string, required bool) string {
-	if _, ok := s.allMembersByName[unversionedName]; !ok {
+// Get a name for sdk snapshot member. If the member is private then generate a snapshot specific
+// name. As part of the processing this checks to make sure that any required members are part of
+// the snapshot.
+func (s *snapshotBuilder) snapshotSdkMemberName(name string, required bool) string {
+	if _, ok := s.allMembersByName[name]; !ok {
 		if required {
-			s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", unversionedName)
+			s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", name)
 		}
-		return unversionedName
-	}
-	return versionedSdkMemberName(s.ctx, unversionedName, s.version)
-}
-
-func (s *snapshotBuilder) versionedSdkMemberNames(members []string, required bool) []string {
-	var references []string = nil
-	for _, m := range members {
-		references = append(references, s.versionedSdkMemberName(m, required))
-	}
-	return references
-}
-
-// Get an internal name unique to the sdk.
-func (s *snapshotBuilder) unversionedSdkMemberName(unversionedName string, required bool) string {
-	if _, ok := s.allMembersByName[unversionedName]; !ok {
-		if required {
-			s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", unversionedName)
-		}
-		return unversionedName
+		return name
 	}
 
-	if s.isInternalMember(unversionedName) {
-		return s.ctx.ModuleName() + "_" + unversionedName
+	if s.isInternalMember(name) {
+		return s.ctx.ModuleName() + "_" + name
 	} else {
-		return unversionedName
+		return name
 	}
 }
 
-func (s *snapshotBuilder) unversionedSdkMemberNames(members []string, required bool) []string {
+func (s *snapshotBuilder) snapshotSdkMemberNames(members []string, required bool) []string {
 	var references []string = nil
 	for _, m := range members {
-		references = append(references, s.unversionedSdkMemberName(m, required))
+		references = append(references, s.snapshotSdkMemberName(m, required))
 	}
 	return references
 }
@@ -1351,6 +1251,11 @@
 	// The variant that is added to the sdk.
 	variant android.SdkAware
 
+	// The optional container of this member, i.e. the module that is depended upon by the sdk
+	// (possibly transitively) and whose dependency on this module is why it was added to the sdk.
+	// Is nil if this a direct dependency of the sdk.
+	container android.SdkAware
+
 	// True if the member should be exported, i.e. accessible, from outside the sdk.
 	export bool
 
@@ -1417,6 +1322,119 @@
 	}
 }
 
+// TODO(187910671): BEGIN - Remove once modules do not have an APEX and default variant.
+// variantCoordinate contains the coordinates used to identify a variant of an SDK member.
+type variantCoordinate struct {
+	// osType identifies the OS target of a variant.
+	osType android.OsType
+	// archId identifies the architecture and whether it is for the native bridge.
+	archId archId
+	// image is the image variant name.
+	image string
+	// linkType is the link type name.
+	linkType string
+}
+
+func getVariantCoordinate(ctx *memberContext, variant android.Module) variantCoordinate {
+	linkType := ""
+	if len(ctx.MemberType().SupportedLinkages()) > 0 {
+		linkType = getLinkType(variant)
+	}
+	return variantCoordinate{
+		osType:   variant.Target().Os,
+		archId:   archIdFromTarget(variant.Target()),
+		image:    variant.ImageVariation().Variation,
+		linkType: linkType,
+	}
+}
+
+// selectApexVariantsWhereAvailable filters the input list of variants by selecting the APEX
+// specific variant for a specific variantCoordinate when there is both an APEX and default variant.
+//
+// There is a long-standing issue where a module that is added to an APEX has both an APEX and
+// default/platform variant created even when the module does not require a platform variant. As a
+// result an indirect dependency onto a module via the APEX will use the APEX variant, whereas a
+// direct dependency onto the module will use the default/platform variant. That would result in a
+// failure while attempting to optimize the properties for a member as it would have two variants
+// when only one was expected.
+//
+// This function mitigates that problem by detecting when there are two variants that differ only
+// by apex variant, where one is the default/platform variant and one is the APEX variant. In that
+// case it picks the APEX variant. It picks the APEX variant because that is the behavior that would
+// be expected
+func selectApexVariantsWhereAvailable(ctx *memberContext, variants []android.SdkAware) []android.SdkAware {
+	moduleCtx := ctx.sdkMemberContext
+
+	// Group the variants by coordinates.
+	variantsByCoord := make(map[variantCoordinate][]android.SdkAware)
+	for _, variant := range variants {
+		coord := getVariantCoordinate(ctx, variant)
+		variantsByCoord[coord] = append(variantsByCoord[coord], variant)
+	}
+
+	toDiscard := make(map[android.SdkAware]struct{})
+	for coord, list := range variantsByCoord {
+		count := len(list)
+		if count == 1 {
+			continue
+		}
+
+		variantsByApex := make(map[string]android.SdkAware)
+		conflictDetected := false
+		for _, variant := range list {
+			apexInfo := moduleCtx.OtherModuleProvider(variant, android.ApexInfoProvider).(android.ApexInfo)
+			apexVariationName := apexInfo.ApexVariationName
+			// If there are two variants for a specific APEX variation then there is conflict.
+			if _, ok := variantsByApex[apexVariationName]; ok {
+				conflictDetected = true
+				break
+			}
+			variantsByApex[apexVariationName] = variant
+		}
+
+		// If there are more than 2 apex variations or one of the apex variations is not the
+		// default/platform variation then there is a conflict.
+		if len(variantsByApex) != 2 {
+			conflictDetected = true
+		} else if _, ok := variantsByApex[""]; !ok {
+			conflictDetected = true
+		}
+
+		// If there are no conflicts then add the default/platform variation to the list to remove.
+		if !conflictDetected {
+			toDiscard[variantsByApex[""]] = struct{}{}
+			continue
+		}
+
+		// There are duplicate variants at this coordinate and they are not the default and APEX variant
+		// so fail.
+		variantDescriptions := []string{}
+		for _, m := range list {
+			variantDescriptions = append(variantDescriptions, fmt.Sprintf("    %s", m.String()))
+		}
+
+		moduleCtx.ModuleErrorf("multiple conflicting variants detected for OsType{%s}, %s, Image{%s}, Link{%s}\n%s",
+			coord.osType, coord.archId.String(), coord.image, coord.linkType,
+			strings.Join(variantDescriptions, "\n"))
+	}
+
+	// If there are any variants to discard then remove them from the list of variants, while
+	// preserving the order.
+	if len(toDiscard) > 0 {
+		filtered := []android.SdkAware{}
+		for _, variant := range variants {
+			if _, ok := toDiscard[variant]; !ok {
+				filtered = append(filtered, variant)
+			}
+		}
+		variants = filtered
+	}
+
+	return variants
+}
+
+// TODO(187910671): END - Remove once modules do not have an APEX and default variant.
+
 type baseInfo struct {
 	Properties android.SdkMemberProperties
 }
@@ -1472,7 +1490,14 @@
 
 	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)))
+			variants := []string{}
+			for _, m := range osTypeVariants {
+				variants = append(variants, fmt.Sprintf("    %s", m.String()))
+			}
+			panic(fmt.Errorf("expected to only have 1 variant of %q when arch type is common but found %d\n%s",
+				ctx.Name(),
+				len(osTypeVariants),
+				strings.Join(variants, "\n")))
 		}
 
 		// A common arch type only has one variant and its properties should be treated
@@ -1670,7 +1695,9 @@
 	// added.
 	archInfo.Properties = variantPropertiesFactory()
 
-	if len(archVariants) == 1 {
+	// if there are multiple supported link variants, we want to nest based on linkage even if there
+	// is only one variant, otherwise, if there is only one variant we can populate based on the arch
+	if len(archVariants) == 1 && len(ctx.MemberType().SupportedLinkages()) <= 1 {
 		archInfo.Properties.PopulateFromVariant(ctx, archVariants[0])
 	} else {
 		// Group the variants by image type.
@@ -1797,11 +1824,13 @@
 	// Create the properties into which the image variant specific properties will be added.
 	imageInfo.Properties = variantPropertiesFactory()
 
-	if len(imageVariants) == 1 {
+	// if there are multiple supported link variants, we want to nest even if there is only one
+	// variant, otherwise, if there is only one variant we can populate based on the image
+	if len(imageVariants) == 1 && len(ctx.MemberType().SupportedLinkages()) <= 1 {
 		imageInfo.Properties.PopulateFromVariant(ctx, imageVariants[0])
 	} else {
 		// There is more than one variant for this image variant which must be differentiated by link
-		// type.
+		// type. Or there are multiple supported linkages and we need to nest based on link type.
 		for _, linkVariant := range imageVariants {
 			linkType := getLinkType(linkVariant)
 			if linkType == "" {
@@ -1845,10 +1874,22 @@
 
 	addSdkMemberPropertiesToSet(ctx, imageInfo.Properties, propertySet)
 
+	usedLinkages := make(map[string]bool, len(imageInfo.linkInfos))
 	for _, linkInfo := range imageInfo.linkInfos {
+		usedLinkages[linkInfo.linkType] = true
 		linkInfo.addToPropertySet(ctx, propertySet)
 	}
 
+	// If not all supported linkages had existing variants, we need to disable the unsupported variant
+	if len(imageInfo.linkInfos) < len(ctx.MemberType().SupportedLinkages()) {
+		for _, l := range ctx.MemberType().SupportedLinkages() {
+			if _, ok := usedLinkages[l]; !ok {
+				otherLinkagePropertySet := propertySet.AddPropertySet(l)
+				otherLinkagePropertySet.AddProperty("enabled", false)
+			}
+		}
+	}
+
 	// If this is for a non-core image variant then make sure that the property set does not contain
 	// any properties as providing non-core image variant specific properties for prebuilts is not
 	// currently supported.
@@ -1942,7 +1983,8 @@
 	memberType := member.memberType
 
 	// Do not add the prefer property if the member snapshot module is a source module type.
-	config := ctx.sdkMemberContext.Config()
+	moduleCtx := ctx.sdkMemberContext
+	config := moduleCtx.Config()
 	if !memberType.UsesSourceModuleTypeInSnapshot() {
 		// Set the prefer based on the environment variable. This is a temporary work around to allow a
 		// snapshot to be created that sets prefer: true.
@@ -1967,9 +2009,10 @@
 		}
 	}
 
+	variants := selectApexVariantsWhereAvailable(ctx, member.variants)
+
 	// Group the variants by os type.
 	variantsByOsType := make(map[android.OsType][]android.Module)
-	variants := member.Variants()
 	for _, variant := range variants {
 		osType := variant.Target().Os
 		variantsByOsType[osType] = append(variantsByOsType[osType], variant)
@@ -2015,7 +2058,7 @@
 	}
 
 	// Extract properties which are common across all architectures and os types.
-	extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, commonProperties, osSpecificPropertiesContainers)
+	extractCommonProperties(moduleCtx, commonValueExtractor, commonProperties, osSpecificPropertiesContainers)
 
 	// Add the common properties to the module.
 	addSdkMemberPropertiesToSet(ctx, commonProperties, bpModule)
diff --git a/ui/build/build.go b/ui/build/build.go
index ec42b70..aadf4af 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -266,7 +266,6 @@
 	}
 
 	if config.StartRBE() {
-		cleanupRBELogsDir(ctx, config)
 		startRBE(ctx, config)
 		defer DumpRBEMetrics(ctx, config, filepath.Join(config.LogsDir(), "rbe_metrics.pb"))
 	}
diff --git a/ui/build/config.go b/ui/build/config.go
index 5765f21..e271bfc 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -19,14 +19,12 @@
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
-	"math/rand"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"runtime"
 	"strconv"
 	"strings"
-	"syscall"
 	"time"
 
 	"android/soong/shared"
@@ -44,15 +42,6 @@
 	envConfigFetchTimeout = 10 * time.Second
 )
 
-var (
-	rbeRandPrefix int
-)
-
-func init() {
-	rand.Seed(time.Now().UnixNano())
-	rbeRandPrefix = rand.Intn(1000)
-}
-
 type Config struct{ *configImpl }
 
 type configImpl struct {
@@ -1155,25 +1144,34 @@
 	return true
 }
 
-func (c *configImpl) rbeProxyLogsDir() string {
-	for _, f := range []string{"RBE_proxy_log_dir", "FLAG_output_dir"} {
+func (c *configImpl) rbeLogDir() string {
+	for _, f := range []string{"RBE_log_dir", "FLAG_log_dir"} {
 		if v, ok := c.environ.Get(f); ok {
 			return v
 		}
 	}
-	buildTmpDir := shared.TempDirForOutDir(c.SoongOutDir())
-	return filepath.Join(buildTmpDir, "rbe")
+	if c.Dist() {
+		return c.LogsDir()
+	}
+	return c.OutDir()
 }
 
-func (c *configImpl) shouldCleanupRBELogsDir() bool {
-	// Perform a log directory cleanup only when the log directory
-	// is auto created by the build rather than user-specified.
-	for _, f := range []string{"RBE_proxy_log_dir", "FLAG_output_dir"} {
-		if _, ok := c.environ.Get(f); ok {
-			return false
+func (c *configImpl) rbeStatsOutputDir() string {
+	for _, f := range []string{"RBE_output_dir", "FLAG_output_dir"} {
+		if v, ok := c.environ.Get(f); ok {
+			return v
 		}
 	}
-	return true
+	return c.rbeLogDir()
+}
+
+func (c *configImpl) rbeLogPath() string {
+	for _, f := range []string{"RBE_log_path", "FLAG_log_path"} {
+		if v, ok := c.environ.Get(f); ok {
+			return v
+		}
+	}
+	return fmt.Sprintf("text://%v/reproxy_log.txt", c.rbeLogDir())
 }
 
 func (c *configImpl) rbeExecRoot() string {
@@ -1225,23 +1223,6 @@
 	return "RBE_use_application_default_credentials", "true"
 }
 
-func (c *configImpl) rbeSockAddr(dir string) (string, error) {
-	maxNameLen := len(syscall.RawSockaddrUnix{}.Path)
-	base := fmt.Sprintf("reproxy_%v.sock", rbeRandPrefix)
-
-	name := filepath.Join(dir, base)
-	if len(name) < maxNameLen {
-		return name, nil
-	}
-
-	name = filepath.Join("/tmp", base)
-	if len(name) < maxNameLen {
-		return name, nil
-	}
-
-	return "", fmt.Errorf("cannot generate a proxy socket address shorter than the limit of %v", maxNameLen)
-}
-
 func (c *configImpl) UseRemoteBuild() bool {
 	return c.UseGoma() || c.UseRBE()
 }
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 3e558f7..8f9a699 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -16,9 +16,12 @@
 
 import (
 	"fmt"
+	"math/rand"
 	"os"
 	"path/filepath"
 	"runtime"
+	"syscall"
+	"time"
 
 	"android/soong/ui/metrics"
 )
@@ -51,16 +54,34 @@
 	return cmdPath
 }
 
+func sockAddr(dir string) (string, error) {
+	maxNameLen := len(syscall.RawSockaddrUnix{}.Path)
+	rand.Seed(time.Now().UnixNano())
+	base := fmt.Sprintf("reproxy_%v.sock", rand.Intn(1000))
+
+	name := filepath.Join(dir, base)
+	if len(name) < maxNameLen {
+		return name, nil
+	}
+
+	name = filepath.Join("/tmp", base)
+	if len(name) < maxNameLen {
+		return name, nil
+	}
+
+	return "", fmt.Errorf("cannot generate a proxy socket address shorter than the limit of %v", maxNameLen)
+}
+
 func getRBEVars(ctx Context, config Config) map[string]string {
 	vars := map[string]string{
-		"RBE_log_dir":    config.rbeProxyLogsDir(),
+		"RBE_log_path":   config.rbeLogPath(),
+		"RBE_log_dir":    config.rbeLogDir(),
 		"RBE_re_proxy":   config.rbeReproxy(),
 		"RBE_exec_root":  config.rbeExecRoot(),
-		"RBE_output_dir": config.rbeProxyLogsDir(),
-		"RBE_proxy_log_dir": config.rbeProxyLogsDir(),
+		"RBE_output_dir": config.rbeStatsOutputDir(),
 	}
 	if config.StartRBE() {
-		name, err := config.rbeSockAddr(absPath(ctx, config.TempDir()))
+		name, err := sockAddr(absPath(ctx, config.TempDir()))
 		if err != nil {
 			ctx.Fatalf("Error retrieving socket address: %v", err)
 			return nil
@@ -79,17 +100,6 @@
 	return vars
 }
 
-func cleanupRBELogsDir(ctx Context, config Config) {
-	if !config.shouldCleanupRBELogsDir() {
-		return
-	}
-
-	rbeTmpDir := config.rbeProxyLogsDir()
-	if err := os.RemoveAll(rbeTmpDir); err != nil {
-		fmt.Fprintln(ctx.Writer, "\033[33mUnable to remove RBE log directory: ", err, "\033[0m")
-	}
-}
-
 func startRBE(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
 	defer ctx.EndTrace()
@@ -100,11 +110,6 @@
 	if n := ulimitOrFatal(ctx, config, "-n"); n < rbeLeastNFiles {
 		ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, rbeLeastNFiles)
 	}
-	if _, err := os.Stat(config.rbeProxyLogsDir()); os.IsNotExist(err) {
-		if err := os.MkdirAll(config.rbeProxyLogsDir(), 0744); err != nil {
-			ctx.Fatalf("Unable to create logs dir (%v) for RBE: %v", config.rbeProxyLogsDir, err)
-		}
-	}
 
 	cmd := Command(ctx, config, "startRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd))
 
@@ -146,7 +151,7 @@
 		return
 	}
 
-	outputDir := config.rbeProxyLogsDir()
+	outputDir := config.rbeStatsOutputDir()
 	if outputDir == "" {
 		ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")
 	}
diff --git a/ui/build/rbe_test.go b/ui/build/rbe_test.go
index 266f76b..8ff96bc 100644
--- a/ui/build/rbe_test.go
+++ b/ui/build/rbe_test.go
@@ -56,8 +56,7 @@
 			env := Environment(tt.env)
 			env.Set("OUT_DIR", tmpDir)
 			env.Set("RBE_DIR", tmpDir)
-			env.Set("RBE_output_dir", tmpDir)
-			env.Set("RBE_proxy_log_dir", tmpDir)
+			env.Set("RBE_output_dir", t.TempDir())
 			config := Config{&configImpl{
 				environ: &env,
 			}}
diff --git a/ui/build/soong.go b/ui/build/soong.go
index c7f22f9..8992b4f 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,7 +15,9 @@
 package build
 
 import (
+	"errors"
 	"fmt"
+	"io/fs"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -491,10 +493,14 @@
 
 	ninja("bootstrap", "bootstrap.ninja", targets...)
 
-	var soongBuildMetrics *soong_metrics_proto.SoongBuildMetrics
 	if shouldCollectBuildSoongMetrics(config) {
 		soongBuildMetrics := loadSoongBuildMetrics(ctx, config)
-		logSoongBuildMetrics(ctx, soongBuildMetrics)
+		if soongBuildMetrics != nil {
+			logSoongBuildMetrics(ctx, soongBuildMetrics)
+			if ctx.Metrics != nil {
+				ctx.Metrics.SetSoongBuildMetrics(soongBuildMetrics)
+			}
+		}
 	}
 
 	distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
@@ -504,9 +510,6 @@
 		distGzipFile(ctx, config, config.SoongMakeVarsMk(), "soong")
 	}
 
-	if shouldCollectBuildSoongMetrics(config) && ctx.Metrics != nil {
-		ctx.Metrics.SetSoongBuildMetrics(soongBuildMetrics)
-	}
 	if config.JsonModuleGraph() {
 		distGzipFile(ctx, config, config.ModuleGraphFile(), "soong")
 	}
@@ -538,8 +541,12 @@
 
 func loadSoongBuildMetrics(ctx Context, config Config) *soong_metrics_proto.SoongBuildMetrics {
 	soongBuildMetricsFile := filepath.Join(config.LogsDir(), "soong_build_metrics.pb")
-	buf, err := ioutil.ReadFile(soongBuildMetricsFile)
-	if err != nil {
+	buf, err := os.ReadFile(soongBuildMetricsFile)
+	if errors.Is(err, fs.ErrNotExist) {
+		// Soong may not have run during this invocation
+          ctx.Verbosef("Failed to read metrics file, %s: %s", soongBuildMetricsFile, err)
+		return nil
+	} else if err != nil {
 		ctx.Fatalf("Failed to load %s: %s", soongBuildMetricsFile, err)
 	}
 	soongBuildMetrics := &soong_metrics_proto.SoongBuildMetrics{}