Merge "Do not pass --compiler-filter=speed-profile with --profile-file."
diff --git a/android/apex.go b/android/apex.go
index b9efe4e..6b4054d 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -854,7 +854,6 @@
 	}
 	return list
 }(map[string]int{
-	"adbd":                                                     30,
 	"android.net.ipsec.ike":                                    30,
 	"androidx.annotation_annotation-nodeps":                    29,
 	"androidx.arch.core_core-common-nodeps":                    29,
@@ -879,18 +878,6 @@
 	"kotlinx-coroutines-android-nodeps":                        30,
 	"kotlinx-coroutines-core":                                  28,
 	"kotlinx-coroutines-core-nodeps":                           30,
-	"libadb_crypto":                                            30,
-	"libadb_pairing_auth":                                      30,
-	"libadb_pairing_connection":                                30,
-	"libadb_pairing_server":                                    30,
-	"libadb_protos":                                            30,
-	"libadb_tls_connection":                                    30,
-	"libadbconnection_client":                                  30,
-	"libadbconnection_server":                                  30,
-	"libadbd_core":                                             30,
-	"libadbd_services":                                         30,
-	"libadbd":                                                  30,
-	"libapp_processes_protos_lite":                             30,
 	"libasyncio":                                               30,
 	"libbrotli":                                                30,
 	"libbuildversion":                                          30,
diff --git a/android/bazel.go b/android/bazel.go
index bf214a5..e97acff 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -15,7 +15,6 @@
 package android
 
 import (
-	"android/soong/bazel"
 	"fmt"
 	"io/ioutil"
 	"path/filepath"
@@ -25,18 +24,35 @@
 	"github.com/google/blueprint/proptools"
 )
 
+type bazelModuleProperties struct {
+	// The label of the Bazel target replacing this Soong module. When run in conversion mode, this
+	// will import the handcrafted build target into the autogenerated file. Note: this may result in
+	// a conflict due to duplicate targets if bp2build_available is also set.
+	Label *string
+
+	// If true, bp2build will generate the converted Bazel target for this module. Note: this may
+	// cause a conflict due to the duplicate targets if label is also set.
+	//
+	// This is a bool pointer to support tristates: true, false, not set.
+	//
+	// To opt-in a module, set bazel_module: { bp2build_available: true }
+	// To opt-out a module, set bazel_module: { bp2build_available: false }
+	// To defer the default setting for the directory, do not set the value.
+	Bp2build_available *bool
+}
+
 // Properties contains common module properties for Bazel migration purposes.
 type properties struct {
 	// In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
 	// this Soong module.
-	Bazel_module bazel.BazelModuleProperties
+	Bazel_module bazelModuleProperties
 }
 
 // namespacedVariableProperties is a map from a string representing a Soong
-// config variable namespace, like "android" or "vendor_name" to a struct
-// pointer representing the soong_config_variables property of a module created
-// by a soong_config_module_type or soong_config_module_type_import.
-type namespacedVariableProperties map[string]interface{}
+// config variable namespace, like "android" or "vendor_name" to a slice of
+// pointer to a struct containing a single field called Soong_config_variables
+// whose value mirrors the structure in the Blueprint file.
+type namespacedVariableProperties map[string][]interface{}
 
 // BazelModuleBase contains the property structs with metadata for modules which can be converted to
 // Bazel.
@@ -68,11 +84,19 @@
 	convertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool
 	GetBazelBuildFileContents(c Config, path, name string) (string, error)
 
-	// For namespaced config variable support
+	// namespacedVariableProps is a map from a soong config variable namespace
+	// (e.g. acme, android) to a map of interfaces{}, which are really
+	// reflect.Struct pointers, representing the value of the
+	// soong_config_variables property of a module. The struct pointer is the
+	// one with the single member called Soong_config_variables, which itself is
+	// a struct containing fields for each supported feature in that namespace.
+	//
+	// The reason for using an slice of interface{} is to support defaults
+	// propagation of the struct pointers.
 	namespacedVariableProps() namespacedVariableProperties
 	setNamespacedVariableProps(props namespacedVariableProperties)
 	BaseModuleType() string
-	SetBaseModuleType(string)
+	SetBaseModuleType(baseModuleType string)
 }
 
 // BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
@@ -237,6 +261,7 @@
 		"packages/modules/adb/proto":                         Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb/tls":                           Bp2BuildDefaultTrueRecursively,
 		"prebuilts/clang/host/linux-x86":                     Bp2BuildDefaultTrueRecursively,
+		"system/apex":                                        Bp2BuildDefaultFalse, // TODO(b/207466993): flaky failures
 		"system/core/diagnose_usb":                           Bp2BuildDefaultTrueRecursively,
 		"system/core/libasyncio":                             Bp2BuildDefaultTrue,
 		"system/core/libcrypto_utils":                        Bp2BuildDefaultTrueRecursively,
diff --git a/android/config.go b/android/config.go
index 6f05565..ed63ddf 100644
--- a/android/config.go
+++ b/android/config.go
@@ -155,9 +155,10 @@
 	fs         pathtools.FileSystem
 	mockBpList string
 
-	runningAsBp2Build        bool
-	bp2buildPackageConfig    Bp2BuildConfig
-	bp2buildModuleTypeConfig map[string]bool
+	runningAsBp2Build              bool
+	bp2buildPackageConfig          Bp2BuildConfig
+	bp2buildModuleTypeConfig       map[string]bool
+	Bp2buildSoongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions
 
 	// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
 	// in tests when a path doesn't exist.
@@ -323,7 +324,7 @@
 			DeviceName:                        stringPtr("test_device"),
 			Platform_sdk_version:              intPtr(30),
 			Platform_sdk_codename:             stringPtr("S"),
-			Platform_version_active_codenames: []string{"S"},
+			Platform_version_active_codenames: []string{"S", "Tiramisu"},
 			DeviceSystemSdkVersions:           []string{"14", "15"},
 			Platform_systemsdk_versions:       []string{"29", "30"},
 			AAPTConfig:                        []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
@@ -855,7 +856,7 @@
 // Returns true if building apps that aren't bundled with the platform.
 // UnbundledBuild() is always true when this is true.
 func (c *config) UnbundledBuildApps() bool {
-	return Bool(c.productVariables.Unbundled_build_apps)
+	return len(c.productVariables.Unbundled_build_apps) > 0
 }
 
 // Returns true if building image that aren't bundled with the platform.
diff --git a/android/defaults.go b/android/defaults.go
index be80cf1..9046002 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -213,10 +213,60 @@
 
 var _ Defaults = (*DefaultsModuleBase)(nil)
 
+// applyNamespacedVariableDefaults only runs in bp2build mode for
+// defaultable/defaults modules. Its purpose is to merge namespaced product
+// variable props from defaults deps, even if those defaults are custom module
+// types created from soong_config_module_type, e.g. one that's wrapping a
+// cc_defaults or java_defaults.
+func applyNamespacedVariableDefaults(defaultDep Defaults, ctx TopDownMutatorContext) {
+	var dep, b Bazelable
+
+	dep, ok := defaultDep.(Bazelable)
+	if !ok {
+		if depMod, ok := defaultDep.(Module); ok {
+			// Track that this dependency hasn't been converted to bp2build yet.
+			ctx.AddUnconvertedBp2buildDep(depMod.Name())
+			return
+		} else {
+			panic("Expected default dep to be a Module.")
+		}
+	}
+
+	b, ok = ctx.Module().(Bazelable)
+	if !ok {
+		return
+	}
+
+	// namespacedVariableProps is a map from namespaces (e.g. acme, android,
+	// vendor_foo) to a slice of soong_config_variable struct pointers,
+	// containing properties for that particular module.
+	src := dep.namespacedVariableProps()
+	dst := b.namespacedVariableProps()
+	if dst == nil {
+		dst = make(namespacedVariableProperties)
+	}
+
+	// Propagate all soong_config_variable structs from the dep. We'll merge the
+	// actual property values later in variable.go.
+	for namespace := range src {
+		if dst[namespace] == nil {
+			dst[namespace] = []interface{}{}
+		}
+		for _, i := range src[namespace] {
+			dst[namespace] = append(dst[namespace], i)
+		}
+	}
+
+	b.setNamespacedVariableProps(dst)
+}
+
 func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext,
 	defaultsList []Defaults) {
 
 	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)
diff --git a/android/hooks.go b/android/hooks.go
index 85fc081..9eaa1ac 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -65,10 +65,10 @@
 	return l.bp.ModuleFactories()
 }
 
-func (l *loadHookContext) AppendProperties(props ...interface{}) {
+func (l *loadHookContext) appendPrependHelper(props []interface{},
+	extendFn func([]interface{}, interface{}, proptools.ExtendPropertyFilterFunc) error) {
 	for _, p := range props {
-		err := proptools.AppendMatchingProperties(l.Module().base().customizableProperties,
-			p, nil)
+		err := extendFn(l.Module().base().customizableProperties, p, nil)
 		if err != nil {
 			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
 				l.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
@@ -78,19 +78,12 @@
 		}
 	}
 }
+func (l *loadHookContext) AppendProperties(props ...interface{}) {
+	l.appendPrependHelper(props, proptools.AppendMatchingProperties)
+}
 
 func (l *loadHookContext) PrependProperties(props ...interface{}) {
-	for _, p := range props {
-		err := proptools.PrependMatchingProperties(l.Module().base().customizableProperties,
-			p, nil)
-		if err != nil {
-			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-				l.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
-			} else {
-				panic(err)
-			}
-		}
-	}
+	l.appendPrependHelper(props, proptools.PrependMatchingProperties)
 }
 
 func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
diff --git a/android/module.go b/android/module.go
index e100330..4a388b5 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1148,7 +1148,7 @@
 //         }
 //     }
 //
-//     func NewMyModule() android.Module) {
+//     func NewMyModule() android.Module {
 //         m := &myModule{}
 //         m.AddProperties(&m.properties)
 //         android.InitAndroidModule(m)
diff --git a/android/mutator.go b/android/mutator.go
index 4b37377..461cb17 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -529,10 +529,10 @@
 	mod.base().addBp2buildInfo(info)
 }
 
-func (t *topDownMutatorContext) AppendProperties(props ...interface{}) {
+func (t *topDownMutatorContext) appendPrependHelper(props []interface{},
+	extendFn func([]interface{}, interface{}, proptools.ExtendPropertyFilterFunc) error) {
 	for _, p := range props {
-		err := proptools.AppendMatchingProperties(t.Module().base().customizableProperties,
-			p, nil)
+		err := extendFn(t.Module().base().customizableProperties, p, nil)
 		if err != nil {
 			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
 				t.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
@@ -543,18 +543,12 @@
 	}
 }
 
+func (t *topDownMutatorContext) AppendProperties(props ...interface{}) {
+	t.appendPrependHelper(props, proptools.AppendMatchingProperties)
+}
+
 func (t *topDownMutatorContext) PrependProperties(props ...interface{}) {
-	for _, p := range props {
-		err := proptools.PrependMatchingProperties(t.Module().base().customizableProperties,
-			p, nil)
-		if err != nil {
-			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-				t.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
-			} else {
-				panic(err)
-			}
-		}
-	}
+	t.appendPrependHelper(props, proptools.PrependMatchingProperties)
 }
 
 // android.topDownMutatorContext either has to embed blueprint.TopDownMutatorContext, in which case every method that
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 065440d..91bbce6 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -380,6 +380,9 @@
 		}
 
 		mtDef, errs := soongconfig.Parse(r, from)
+		if ctx.Config().runningAsBp2Build {
+			ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(*mtDef)
+		}
 
 		if len(errs) > 0 {
 			reportErrors(ctx, from, errs...)
@@ -415,14 +418,13 @@
 	if !conditionalFactoryProps.IsValid() {
 		return factory
 	}
-	useBp2buildHook := bp2build && proptools.BoolDefault(moduleType.Bp2buildAvailable, false)
 
 	return func() (blueprint.Module, []interface{}) {
 		module, props := factory()
 		conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
 		props = append(props, conditionalProps.Interface())
 
-		if useBp2buildHook {
+		if bp2build {
 			// The loadhook is different for bp2build, since we don't want to set a specific
 			// set of property values based on a vendor var -- we want __all of them__ to
 			// generate select statements, so we put the entire soong_config_variables
@@ -434,7 +436,7 @@
 					// Instead of applying all properties, keep the entire conditionalProps struct as
 					// part of the custom module so dependent modules can create the selects accordingly
 					m.setNamespacedVariableProps(namespacedVariableProperties{
-						moduleType.ConfigNamespace: conditionalProps.Interface(),
+						moduleType.ConfigNamespace: []interface{}{conditionalProps.Interface()},
 					})
 				}
 			})
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 1af89ba..09a5057 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -15,12 +15,12 @@
 package soongconfig
 
 import (
-	"android/soong/bazel"
 	"fmt"
 	"io"
 	"reflect"
 	"sort"
 	"strings"
+	"sync"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/parser"
@@ -121,8 +121,6 @@
 
 	// the list of properties that this module type will extend.
 	Properties []string
-
-	Bazel_module bazel.BazelModuleProperties
 }
 
 func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
@@ -233,6 +231,104 @@
 	variables map[string]soongConfigVariable
 }
 
+// Bp2BuildSoongConfigDefinition keeps a global record of all soong config
+// string vars, bool vars and value vars created by every
+// soong_config_module_type in this build.
+type Bp2BuildSoongConfigDefinitions struct {
+	StringVars map[string]map[string]bool
+	BoolVars   map[string]bool
+	ValueVars  map[string]bool
+}
+
+var bp2buildSoongConfigVarsLock sync.Mutex
+
+// SoongConfigVariablesForBp2build extracts information from a
+// SoongConfigDefinition that bp2build needs to generate constraint settings and
+// values for, in order to migrate soong_config_module_type usages to Bazel.
+func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef SoongConfigDefinition) {
+	// In bp2build mode, this method is called concurrently in goroutines from
+	// loadhooks while parsing soong_config_module_type, so add a mutex to
+	// prevent concurrent map writes. See b/207572723
+	bp2buildSoongConfigVarsLock.Lock()
+	defer bp2buildSoongConfigVarsLock.Unlock()
+
+	if defs.StringVars == nil {
+		defs.StringVars = make(map[string]map[string]bool)
+	}
+	if defs.BoolVars == nil {
+		defs.BoolVars = make(map[string]bool)
+	}
+	if defs.ValueVars == nil {
+		defs.ValueVars = make(map[string]bool)
+	}
+	for _, moduleType := range mtDef.ModuleTypes {
+		for _, v := range moduleType.Variables {
+			key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
+			if strVar, ok := v.(*stringVariable); ok {
+				if _, ok := defs.StringVars[key]; !ok {
+					defs.StringVars[key] = make(map[string]bool, 0)
+				}
+				for _, value := range strVar.values {
+					defs.StringVars[key][value] = true
+				}
+			} else if _, ok := v.(*boolVariable); ok {
+				defs.BoolVars[key] = true
+			} else if _, ok := v.(*valueVariable); ok {
+				defs.ValueVars[key] = true
+			} else {
+				panic(fmt.Errorf("Unsupported variable type: %+v", v))
+			}
+		}
+	}
+}
+
+// This is a copy of the one available in soong/android/util.go, but depending
+// on the android package causes a cyclic dependency. A refactoring here is to
+// extract common utils out from android/utils.go for other packages like this.
+func sortedStringKeys(m interface{}) []string {
+	v := reflect.ValueOf(m)
+	if v.Kind() != reflect.Map {
+		panic(fmt.Sprintf("%#v is not a map", m))
+	}
+	keys := v.MapKeys()
+	s := make([]string, 0, len(keys))
+	for _, key := range keys {
+		s = append(s, key.String())
+	}
+	sort.Strings(s)
+	return s
+}
+
+// String emits the Soong config variable definitions as Starlark dictionaries.
+func (defs Bp2BuildSoongConfigDefinitions) String() string {
+	ret := ""
+	ret += "soong_config_bool_variables = {\n"
+	for _, boolVar := range sortedStringKeys(defs.BoolVars) {
+		ret += fmt.Sprintf("    \"%s\": True,\n", boolVar)
+	}
+	ret += "}\n"
+	ret += "\n"
+
+	ret += "soong_config_value_variables = {\n"
+	for _, valueVar := range sortedStringKeys(defs.ValueVars) {
+		ret += fmt.Sprintf("    \"%s\": True,\n", valueVar)
+	}
+	ret += "}\n"
+	ret += "\n"
+
+	ret += "soong_config_string_variables = {\n"
+	for _, stringVar := range sortedStringKeys(defs.StringVars) {
+		ret += fmt.Sprintf("    \"%s\": [\n", stringVar)
+		for _, choice := range sortedStringKeys(defs.StringVars[stringVar]) {
+			ret += fmt.Sprintf("        \"%s\",\n", choice)
+		}
+		ret += fmt.Sprintf("    ],\n")
+	}
+	ret += "}"
+
+	return ret
+}
+
 // CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
 // property layout for the Soong config variables, with each possible value an interface{} that
 // contains a nil pointer to another newly constructed type that contains the affectable properties.
@@ -436,7 +532,6 @@
 
 	affectableProperties []string
 	variableNames        []string
-	Bp2buildAvailable    *bool
 }
 
 func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
@@ -445,7 +540,6 @@
 		ConfigNamespace:      props.Config_namespace,
 		BaseModuleType:       props.Module_type,
 		variableNames:        props.Variables,
-		Bp2buildAvailable:    props.Bazel_module.Bp2build_available,
 	}
 
 	for _, name := range props.Bool_variables {
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index 48cdfe7..b14f8b4 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -364,3 +364,117 @@
 		}
 	}
 }
+
+func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
+	testCases := []struct {
+		defs     Bp2BuildSoongConfigDefinitions
+		expected string
+	}{
+		{
+			defs: Bp2BuildSoongConfigDefinitions{},
+			expected: `soong_config_bool_variables = {
+}
+
+soong_config_value_variables = {
+}
+
+soong_config_string_variables = {
+}`}, {
+			defs: Bp2BuildSoongConfigDefinitions{
+				BoolVars: map[string]bool{
+					"bool_var": true,
+				},
+			},
+			expected: `soong_config_bool_variables = {
+    "bool_var": True,
+}
+
+soong_config_value_variables = {
+}
+
+soong_config_string_variables = {
+}`}, {
+			defs: Bp2BuildSoongConfigDefinitions{
+				ValueVars: map[string]bool{
+					"value_var": true,
+				},
+			},
+			expected: `soong_config_bool_variables = {
+}
+
+soong_config_value_variables = {
+    "value_var": True,
+}
+
+soong_config_string_variables = {
+}`}, {
+			defs: Bp2BuildSoongConfigDefinitions{
+				StringVars: map[string]map[string]bool{
+					"string_var": map[string]bool{
+						"choice1": true,
+						"choice2": true,
+						"choice3": true,
+					},
+				},
+			},
+			expected: `soong_config_bool_variables = {
+}
+
+soong_config_value_variables = {
+}
+
+soong_config_string_variables = {
+    "string_var": [
+        "choice1",
+        "choice2",
+        "choice3",
+    ],
+}`}, {
+			defs: Bp2BuildSoongConfigDefinitions{
+				BoolVars: map[string]bool{
+					"bool_var_one": true,
+				},
+				ValueVars: map[string]bool{
+					"value_var_one": true,
+					"value_var_two": true,
+				},
+				StringVars: map[string]map[string]bool{
+					"string_var_one": map[string]bool{
+						"choice1": true,
+						"choice2": true,
+						"choice3": true,
+					},
+					"string_var_two": map[string]bool{
+						"foo": true,
+						"bar": true,
+					},
+				},
+			},
+			expected: `soong_config_bool_variables = {
+    "bool_var_one": True,
+}
+
+soong_config_value_variables = {
+    "value_var_one": True,
+    "value_var_two": True,
+}
+
+soong_config_string_variables = {
+    "string_var_one": [
+        "choice1",
+        "choice2",
+        "choice3",
+    ],
+    "string_var_two": [
+        "bar",
+        "foo",
+    ],
+}`},
+	}
+	for _, test := range testCases {
+		actual := test.defs.String()
+		if actual != test.expected {
+			t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
+		}
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index 89cd59e..a29c6f8 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -15,13 +15,14 @@
 package android
 
 import (
-	"android/soong/android/soongconfig"
-	"android/soong/bazel"
 	"fmt"
 	"reflect"
 	"runtime"
 	"strings"
 
+	"android/soong/android/soongconfig"
+	"android/soong/bazel"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -245,30 +246,30 @@
 
 	AppsDefaultVersionName *string `json:",omitempty"`
 
-	Allow_missing_dependencies   *bool `json:",omitempty"`
-	Unbundled_build              *bool `json:",omitempty"`
-	Unbundled_build_apps         *bool `json:",omitempty"`
-	Unbundled_build_image        *bool `json:",omitempty"`
-	Always_use_prebuilt_sdks     *bool `json:",omitempty"`
-	Skip_boot_jars_check         *bool `json:",omitempty"`
-	Malloc_not_svelte            *bool `json:",omitempty"`
-	Malloc_zero_contents         *bool `json:",omitempty"`
-	Malloc_pattern_fill_contents *bool `json:",omitempty"`
-	Safestack                    *bool `json:",omitempty"`
-	HostStaticBinaries           *bool `json:",omitempty"`
-	Binder32bit                  *bool `json:",omitempty"`
-	UseGoma                      *bool `json:",omitempty"`
-	UseRBE                       *bool `json:",omitempty"`
-	UseRBEJAVAC                  *bool `json:",omitempty"`
-	UseRBER8                     *bool `json:",omitempty"`
-	UseRBED8                     *bool `json:",omitempty"`
-	Debuggable                   *bool `json:",omitempty"`
-	Eng                          *bool `json:",omitempty"`
-	Treble_linker_namespaces     *bool `json:",omitempty"`
-	Enforce_vintf_manifest       *bool `json:",omitempty"`
-	Uml                          *bool `json:",omitempty"`
-	Arc                          *bool `json:",omitempty"`
-	MinimizeJavaDebugInfo        *bool `json:",omitempty"`
+	Allow_missing_dependencies   *bool    `json:",omitempty"`
+	Unbundled_build              *bool    `json:",omitempty"`
+	Unbundled_build_apps         []string `json:",omitempty"`
+	Unbundled_build_image        *bool    `json:",omitempty"`
+	Always_use_prebuilt_sdks     *bool    `json:",omitempty"`
+	Skip_boot_jars_check         *bool    `json:",omitempty"`
+	Malloc_not_svelte            *bool    `json:",omitempty"`
+	Malloc_zero_contents         *bool    `json:",omitempty"`
+	Malloc_pattern_fill_contents *bool    `json:",omitempty"`
+	Safestack                    *bool    `json:",omitempty"`
+	HostStaticBinaries           *bool    `json:",omitempty"`
+	Binder32bit                  *bool    `json:",omitempty"`
+	UseGoma                      *bool    `json:",omitempty"`
+	UseRBE                       *bool    `json:",omitempty"`
+	UseRBEJAVAC                  *bool    `json:",omitempty"`
+	UseRBER8                     *bool    `json:",omitempty"`
+	UseRBED8                     *bool    `json:",omitempty"`
+	Debuggable                   *bool    `json:",omitempty"`
+	Eng                          *bool    `json:",omitempty"`
+	Treble_linker_namespaces     *bool    `json:",omitempty"`
+	Enforce_vintf_manifest       *bool    `json:",omitempty"`
+	Uml                          *bool    `json:",omitempty"`
+	Arc                          *bool    `json:",omitempty"`
+	MinimizeJavaDebugInfo        *bool    `json:",omitempty"`
 
 	Check_elf_files *bool `json:",omitempty"`
 
@@ -564,6 +565,10 @@
 	FullConfig string
 }
 
+func (p *ProductConfigProperty) AlwaysEmit() bool {
+	return p.Namespace != ""
+}
+
 func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
 	if p.Namespace == "" {
 		return bazel.ProductVariableConfigurationAxis(p.FullConfig)
@@ -640,19 +645,135 @@
 	}
 
 	if m, ok := module.(Bazelable); ok && m.namespacedVariableProps() != nil {
-		for namespace, namespacedVariableProp := range m.namespacedVariableProps() {
-			productVariableValues(
-				soongconfig.SoongConfigProperty,
-				namespacedVariableProp,
-				namespace,
-				"",
-				&productConfigProperties)
+		for namespace, namespacedVariableProps := range m.namespacedVariableProps() {
+			for _, namespacedVariableProp := range namespacedVariableProps {
+				productVariableValues(
+					soongconfig.SoongConfigProperty,
+					namespacedVariableProp,
+					namespace,
+					"",
+					&productConfigProperties)
+			}
 		}
 	}
 
+	productConfigProperties.zeroValuesForNamespacedVariables()
+
 	return productConfigProperties
 }
 
+// zeroValuesForNamespacedVariables ensures that selects that contain __only__
+// conditions default values have zero values set for the other non-default
+// values for that select statement.
+//
+// If the ProductConfigProperties map contains these items, as parsed from the .bp file:
+//
+// library_linking_strategy: {
+//     prefer_static: {
+//         static_libs: [
+//             "lib_a",
+//             "lib_b",
+//         ],
+//     },
+//     conditions_default: {
+//         shared_libs: [
+//             "lib_a",
+//             "lib_b",
+//         ],
+//     },
+// },
+//
+// Static_libs {Library_linking_strategy ANDROID prefer_static} [lib_a lib_b]
+// Shared_libs {Library_linking_strategy ANDROID conditions_default} [lib_a lib_b]
+//
+// We need to add this:
+//
+// Shared_libs {Library_linking_strategy ANDROID prefer_static} []
+//
+// so that the following gets generated for the "dynamic_deps" attribute,
+// instead of putting lib_a and lib_b directly into dynamic_deps without a
+// select:
+//
+// dynamic_deps = select({
+//     "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+//     "//conditions:default": [
+//         "//foo/bar:lib_a",
+//         "//foo/bar:lib_b",
+//     ],
+// }),
+func (props *ProductConfigProperties) zeroValuesForNamespacedVariables() {
+	// A map of product config properties to the zero values of their respective
+	// property value.
+	zeroValues := make(map[ProductConfigProperty]interface{})
+
+	// A map of prop names (e.g. cflags) to product config properties where the
+	// (prop name, ProductConfigProperty) tuple contains a non-conditions_default key.
+	//
+	// e.g.
+	//
+	// prefer_static: {
+	//     static_libs: [
+	//         "lib_a",
+	//         "lib_b",
+	//     ],
+	// },
+	// conditions_default: {
+	//     shared_libs: [
+	//         "lib_a",
+	//         "lib_b",
+	//     ],
+	// },
+	//
+	// The tuple of ("static_libs", prefer_static) would be in this map.
+	hasNonDefaultValue := make(map[string]map[ProductConfigProperty]bool)
+
+	// Iterate over all added soong config variables.
+	for propName, v := range *props {
+		for p, intf := range v {
+			if p.Namespace == "" {
+				// If there's no namespace, this isn't a soong config variable,
+				// i.e. this is a product variable. product variables have no
+				// conditions_defaults, so skip them.
+				continue
+			}
+			if p.FullConfig == bazel.ConditionsDefaultConfigKey {
+				// Skip conditions_defaults.
+				continue
+			}
+			if hasNonDefaultValue[propName] == nil {
+				hasNonDefaultValue[propName] = make(map[ProductConfigProperty]bool)
+				hasNonDefaultValue[propName][p] = false
+			}
+			// Create the zero value of the variable.
+			if _, exists := zeroValues[p]; !exists {
+				zeroValue := reflect.Zero(reflect.ValueOf(intf).Type()).Interface()
+				if zeroValue == nil {
+					panic(fmt.Errorf("Expected non-nil zero value for product/config variable %+v\n", intf))
+				}
+				zeroValues[p] = zeroValue
+			}
+			hasNonDefaultValue[propName][p] = true
+		}
+	}
+
+	for propName := range *props {
+		for p, zeroValue := range zeroValues {
+			// Ignore variables that already have a non-default value for that axis
+			if exists, _ := hasNonDefaultValue[propName][p]; !exists {
+				// fmt.Println(propName, p.Namespace, p.Name, p.FullConfig, zeroValue)
+				// Insert the zero value for this propname + product config value.
+				props.AddProductConfigProperty(
+					propName,
+					p.Namespace,
+					p.Name,
+					p.FullConfig,
+					zeroValue,
+				)
+			}
+		}
+	}
+}
+
 func (p *ProductConfigProperties) AddProductConfigProperty(
 	propertyName, namespace, productVariableName, config string, property interface{}) {
 	if (*p)[propertyName] == nil {
@@ -665,7 +786,19 @@
 		FullConfig: config,              // e.g. size, feature1-x86, size__conditions_default
 	}
 
-	(*p)[propertyName][productConfigProp] = property
+	if existing, ok := (*p)[propertyName][productConfigProp]; ok && namespace != "" {
+		switch dst := existing.(type) {
+		case []string:
+			if src, ok := property.([]string); ok {
+				dst = append(dst, src...)
+				(*p)[propertyName][productConfigProp] = dst
+			}
+		default:
+			panic(fmt.Errorf("TODO: handle merging value %s", existing))
+		}
+	} else {
+		(*p)[propertyName][productConfigProp] = property
+	}
 }
 
 var (
@@ -701,19 +834,10 @@
 	return v, true
 }
 
-// productVariableValues uses reflection to convert a property struct for
-// product_variables and soong_config_variables to structs that can be generated
-// as select statements.
-func productVariableValues(
-	fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties) {
-	if suffix != "" {
-		suffix = "-" + suffix
-	}
-
-	// variableValues represent the product_variables or soong_config_variables
-	// struct.
-	variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
-
+func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value) {
+	// variableValues can either be a product_variables or
+	// soong_config_variables struct.
+	//
 	// Example of product_variables:
 	//
 	// product_variables: {
@@ -818,9 +942,10 @@
 						field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"]
 					)
 				}
-			} else {
-				// Not a conditions_default or a struct prop, i.e. regular
-				// product variables, or not a string-typed config var.
+			} else if property.Kind() != reflect.Interface {
+				// If not an interface, then this is not a conditions_default or
+				// a struct prop. That is, this is a regular product variable,
+				// or a bool/value config variable.
 				config := productVariableName + suffix
 				productConfigProperties.AddProductConfigProperty(
 					propertyName,
@@ -834,6 +959,20 @@
 	}
 }
 
+// productVariableValues uses reflection to convert a property struct for
+// product_variables and soong_config_variables to structs that can be generated
+// as select statements.
+func productVariableValues(
+	fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties) {
+	if suffix != "" {
+		suffix = "-" + suffix
+	}
+
+	// variableValues represent the product_variables or soong_config_variables struct.
+	variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
+	productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues)
+}
+
 func VariableMutator(mctx BottomUpMutatorContext) {
 	var module Module
 	var ok bool
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 2048429..7764b6b 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -108,18 +108,6 @@
 		return moduleNames
 	}
 
-	var postInstallCommands []string
-	for _, fi := range a.filesInfo {
-		if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
-			// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
-			linkTarget := filepath.Join("/system", fi.path())
-			linkPath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.path())
-			mkdirCmd := "mkdir -p " + filepath.Dir(linkPath)
-			linkCmd := "ln -sfn " + linkTarget + " " + linkPath
-			postInstallCommands = append(postInstallCommands, mkdirCmd, linkCmd)
-		}
-	}
-
 	seenDataOutPaths := make(map[string]bool)
 
 	for _, fi := range a.filesInfo {
@@ -193,6 +181,8 @@
 			// we will have duplicated notice entries.
 			fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
 		}
+		fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", filepath.Join(modulePath, fi.stem()))
+		fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", fi.builtFile.String()+":"+filepath.Join(modulePath, fi.stem()))
 		fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
 		fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.nameInMake())
 		if fi.module != nil {
@@ -302,18 +292,10 @@
 					if len(patterns) > 0 {
 						fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(patterns, " "))
 					}
-					if len(a.compatSymlinks) > 0 {
-						// For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
-						postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
-					}
 				}
 
 				// File_contexts of flattened APEXes should be merged into file_contexts.bin
 				fmt.Fprintln(w, "LOCAL_FILE_CONTEXTS :=", a.fileContexts)
-
-				if len(postInstallCommands) > 0 {
-					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
-				}
 			}
 			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 		}
@@ -394,6 +376,8 @@
 				}
 				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix)
 				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
+				fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
+				fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
 
 				// Because apex writes .mk with Custom(), we need to write manually some common properties
 				// which are available via data.Entries
@@ -418,16 +402,6 @@
 					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
 				}
 				a.writeRequiredModules(w, name)
-				var postInstallCommands []string
-				if a.prebuiltFileToDelete != "" {
-					postInstallCommands = append(postInstallCommands, "rm -rf "+
-						filepath.Join(a.installDir.ToMakePath().String(), a.prebuiltFileToDelete))
-				}
-				// For unflattened apexes, compat symlinks are attached to apex package itself as LOCAL_POST_INSTALL_CMD
-				postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
-				if len(postInstallCommands) > 0 {
-					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
-				}
 
 				if a.mergedNotices.Merged.Valid() {
 					fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNotices.Merged.Path().String())
@@ -455,7 +429,7 @@
 				}
 
 				distCoverageFiles(w, "ndk_apis_usedby_apex", a.nativeApisUsedByModuleFile.String())
-				distCoverageFiles(w, "ndk_apis_usedby_apex", a.nativeApisBackedByModuleFile.String())
+				distCoverageFiles(w, "ndk_apis_backedby_apex", a.nativeApisBackedByModuleFile.String())
 				distCoverageFiles(w, "java_apis_used_by_apex", a.javaApisUsedByModuleFile.String())
 			}
 		}}
diff --git a/apex/apex.go b/apex/apex.go
index 378efe6..c1a4b40 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -409,14 +409,14 @@
 	// vendor/google/build/build_unbundled_mainline_module.sh for more detail.
 	bundleModuleFile android.WritablePath
 
-	// Target path to install this APEX. Usually out/target/product/<device>/<partition>/apex.
+	// Target directory to install this APEX. Usually out/target/product/<device>/<partition>/apex.
 	installDir android.InstallPath
 
-	// List of commands to create symlinks for backward compatibility. These commands will be
-	// attached as LOCAL_POST_INSTALL_CMD to apex package itself (for unflattened build) or
-	// apex_manifest (for flattened build) so that compat symlinks are always installed
-	// regardless of TARGET_FLATTEN_APEX setting.
-	compatSymlinks []string
+	// Path where this APEX was installed.
+	installedFile android.InstallPath
+
+	// Installed locations of symlinks for backward compatibility.
+	compatSymlinks android.InstallPaths
 
 	// Text file having the list of individual files that are included in this APEX. Used for
 	// debugging purpose.
@@ -442,6 +442,10 @@
 	modulePaths []string
 }
 
+func (*apexBundle) InstallBypassMake() bool {
+	return true
+}
+
 // apexFileClass represents a type of file that can be included in APEX.
 type apexFileClass int
 
@@ -2097,7 +2101,9 @@
 		a.linkToSystemLib = false
 	}
 
-	a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
+	if a.properties.ApexType != zipApex {
+		a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType)
+	}
 
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 4) generate the build rules to create the APEX. This is done in builder.go.
@@ -2662,7 +2668,6 @@
 		"libbuildversion",
 		"libmath",
 		"libprocpartition",
-		"libsync",
 	}
 	//
 	// Module separator
@@ -2770,7 +2775,6 @@
 		"libstagefright_metadatautils",
 		"libstagefright_mpeg2extractor",
 		"libstagefright_mpeg2support",
-		"libsync",
 		"libui",
 		"libui_headers",
 		"libunwindstack",
@@ -2911,7 +2915,6 @@
 		"libstagefright_m4vh263dec",
 		"libstagefright_m4vh263enc",
 		"libstagefright_mp3dec",
-		"libsync",
 		"libui",
 		"libui_headers",
 		"libunwindstack",
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 59ea206..130581c 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -215,7 +215,9 @@
 		variables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
 		variables.Platform_sdk_codename = proptools.StringPtr("Q")
 		variables.Platform_sdk_final = proptools.BoolPtr(false)
-		variables.Platform_version_active_codenames = []string{"Q"}
+		// "Tiramisu" needs to be in the next line for compatibility with soong code,
+		// not because of these tests specifically (it's not used by the tests)
+		variables.Platform_version_active_codenames = []string{"Q", "Tiramisu"}
 		variables.Platform_vndk_version = proptools.StringPtr("29")
 	}),
 )
@@ -6204,7 +6206,7 @@
 	})
 	// Permission XML should point to the activated path of impl jar of java_sdk_library
 	sdkLibrary := ctx.ModuleForTests("foo.xml", "android_common_myapex").Rule("java_sdk_xml")
-	ensureContains(t, sdkLibrary.RuleParams.Command, `<library name=\"foo\" file=\"/apex/myapex/javalib/foo.jar\"`)
+	ensureMatches(t, sdkLibrary.RuleParams.Command, `<library\\n\s+name=\\\"foo\\\"\\n\s+file=\\\"/apex/myapex/javalib/foo.jar\\\"`)
 }
 
 func TestJavaSDKLibrary_WithinApex(t *testing.T) {
diff --git a/apex/builder.go b/apex/builder.go
index 3599c5d..0880e2b 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -414,18 +414,35 @@
 func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
 	apexType := a.properties.ApexType
 	suffix := apexType.suffix()
+	apexName := proptools.StringDefault(a.properties.Apex_name, a.BaseModuleName())
 
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// Step 1: copy built files to appropriate directories under the image directory
 
 	imageDir := android.PathForModuleOut(ctx, "image"+suffix)
 
+	installSymbolFiles := !ctx.Config().KatiEnabled() || a.ExportedToMake()
+
+	// b/140136207. When there are overriding APEXes for a VNDK APEX, the symbols file for the overridden
+	// APEX and the overriding APEX will have the same installation paths at /apex/com.android.vndk.v<ver>
+	// as their apexName will be the same. To avoid the path conflicts, skip installing the symbol files
+	// for the overriding VNDK APEXes.
+	if a.vndkApex && len(a.overridableProperties.Overrides) > 0 {
+		installSymbolFiles = false
+	}
+
+	// Avoid creating duplicate build rules for multi-installed APEXes.
+	if proptools.BoolDefault(a.properties.Multi_install_skip_symbol_files, false) {
+		installSymbolFiles = false
+	}
+
 	// TODO(jiyong): use the RuleBuilder
 	var copyCommands []string
 	var implicitInputs []android.Path
+	pathWhenActivated := android.PathForModuleInPartitionInstall(ctx, "apex", apexName)
 	for _, fi := range a.filesInfo {
 		destPath := imageDir.Join(ctx, fi.path()).String()
-
+		var installedPath android.InstallPath
 		// Prepare the destination path
 		destPathDir := filepath.Dir(destPath)
 		if fi.class == appSet {
@@ -444,10 +461,20 @@
 				copyCommands = append(copyCommands,
 					fmt.Sprintf("unzip -qDD -d %s %s", destPathDir,
 						fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs().String()))
+				if installSymbolFiles {
+					installedPath = ctx.InstallFileWithExtraFilesZip(pathWhenActivated.Join(ctx, fi.installDir),
+						fi.stem(), fi.builtFile, fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs())
+				}
 			} else {
 				copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
+				if installSymbolFiles {
+					installedPath = ctx.InstallFile(pathWhenActivated.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
+				}
 			}
 			implicitInputs = append(implicitInputs, fi.builtFile)
+			if installSymbolFiles {
+				implicitInputs = append(implicitInputs, installedPath)
+			}
 		}
 
 		// Create additional symlinks pointing the file inside the APEX (if any). Note that
@@ -455,6 +482,10 @@
 		for _, symlinkPath := range fi.symlinkPaths() {
 			symlinkDest := imageDir.Join(ctx, symlinkPath).String()
 			copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
+			if installSymbolFiles {
+				installedSymlink := ctx.InstallSymlink(pathWhenActivated.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
+				implicitInputs = append(implicitInputs, installedSymlink)
+			}
 		}
 
 		// Copy the test files (if any)
@@ -473,6 +504,11 @@
 		}
 	}
 	implicitInputs = append(implicitInputs, a.manifestPbOut)
+	if installSymbolFiles {
+		installedManifest := ctx.InstallFile(pathWhenActivated, "apex_manifest.pb", a.manifestPbOut)
+		installedKey := ctx.InstallFile(pathWhenActivated, "apex_pubkey", a.publicKeyFile)
+		implicitInputs = append(implicitInputs, installedManifest, installedKey)
+	}
 
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// Step 1.a: Write the list of files in this APEX to a txt file and compare it against
@@ -841,47 +877,55 @@
 		a.outputFile = signedCompressedOutputFile
 	}
 
+	installSuffix := suffix
+	if a.isCompressed {
+		installSuffix = imageCapexSuffix
+	}
+
 	// Install to $OUT/soong/{target,host}/.../apex.
-	ctx.InstallFile(a.installDir, a.Name()+suffix, a.outputFile)
+	a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
+		a.compatSymlinks.Paths()...)
 
 	// installed-files.txt is dist'ed
 	a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir)
 }
 
-// Context "decorator", overriding the InstallBypassMake method to always reply `true`.
-type flattenedApexContext struct {
-	android.ModuleContext
-}
-
-func (c *flattenedApexContext) InstallBypassMake() bool {
-	return true
-}
-
 // buildFlattenedApex creates rules for a flattened APEX. Flattened APEX actually doesn't have a
 // single output file. It is a phony target for all the files under /system/apex/<name> directory.
 // This function creates the installation rules for the files.
 func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
 	bundleName := a.Name()
+	installedSymlinks := append(android.InstallPaths(nil), a.compatSymlinks...)
 	if a.installable() {
 		for _, fi := range a.filesInfo {
 			dir := filepath.Join("apex", bundleName, fi.installDir)
-			target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.stem(), fi.builtFile)
-			for _, sym := range fi.symlinks {
-				ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
+			installDir := android.PathForModuleInstall(ctx, dir)
+			if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
+				// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
+				pathOnDevice := filepath.Join("/system", fi.path())
+				installedSymlinks = append(installedSymlinks,
+					ctx.InstallAbsoluteSymlink(installDir, fi.stem(), pathOnDevice))
+			} else {
+				target := ctx.InstallFile(installDir, fi.stem(), fi.builtFile)
+				for _, sym := range fi.symlinks {
+					installedSymlinks = append(installedSymlinks,
+						ctx.InstallSymlink(installDir, sym, target))
+				}
 			}
 		}
+
+		// Create install rules for the files added in GenerateAndroidBuildActions after
+		// buildFlattenedApex is called.  Add the links to system libs (if any) as dependencies
+		// of the apex_manifest.pb file since it is always present.
+		dir := filepath.Join("apex", bundleName)
+		installDir := android.PathForModuleInstall(ctx, dir)
+		ctx.InstallFile(installDir, "apex_manifest.pb", a.manifestPbOut, installedSymlinks.Paths()...)
+		ctx.InstallFile(installDir, "apex_pubkey", a.publicKeyFile)
 	}
 
 	a.fileContexts = a.buildFileContexts(ctx)
 
-	// Temporarily wrap the original `ctx` into a `flattenedApexContext` to have it reply true
-	// to `InstallBypassMake()` (thus making the call `android.PathForModuleInstall` below use
-	// `android.pathForInstallInMakeDir` instead of `android.PathForOutput`) to return the
-	// correct path to the flattened APEX (as its contents is installed by Make, not Soong).
-	// TODO(jiyong): Why do we need to set outputFile for flattened APEX? We don't seem to use
-	// it and it actually points to a path that can never be built. Remove this.
-	factx := flattenedApexContext{ctx}
-	a.outputFile = android.PathForModuleInstall(&factx, "apex", bundleName)
+	a.outputFile = android.PathForModuleInstall(ctx, "apex", bundleName)
 }
 
 // getCertificateAndPrivateKey retrieves the cert and the private key that will be used to sign
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 61e7a0b..84bdcdd 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -17,11 +17,13 @@
 import (
 	"fmt"
 	"io"
+	"path/filepath"
 	"strconv"
 	"strings"
 
 	"android/soong/android"
 	"android/soong/java"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -53,18 +55,17 @@
 
 	installDir      android.InstallPath
 	installFilename string
+	installedFile   android.InstallPath
 	outputApex      android.WritablePath
 
 	// A list of apexFile objects created in prebuiltCommon.initApexFilesForAndroidMk which are used
 	// to create make modules in prebuiltCommon.AndroidMkEntries.
 	apexFilesForAndroidMk []apexFile
 
-	// list of commands to create symlinks for backward compatibility.
-	// these commands will be attached as LOCAL_POST_INSTALL_CMD
-	compatSymlinks []string
+	// Installed locations of symlinks for backward compatibility.
+	compatSymlinks android.InstallPaths
 
-	hostRequired        []string
-	postInstallCommands []string
+	hostRequired []string
 }
 
 type sanitizedPrebuilt interface {
@@ -223,13 +224,10 @@
 				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
 					entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
+					entries.SetPath("LOCAL_SOONG_INSTALLED_MODULE", p.installedFile)
+					entries.SetString("LOCAL_SOONG_INSTALL_PAIRS", p.outputApex.String()+":"+p.installedFile.String())
 					entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
 					entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.prebuiltCommonProperties.Overrides...)
-					postInstallCommands := append([]string{}, p.postInstallCommands...)
-					postInstallCommands = append(postInstallCommands, p.compatSymlinks...)
-					if len(postInstallCommands) > 0 {
-						entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(postInstallCommands, " && "))
-					}
 					p.addRequiredModules(entries)
 				},
 			},
@@ -259,6 +257,9 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_SOONG_INSTALLED_MODULE", filepath.Join(p.installDir.String(), fi.stem()))
+				entries.SetString("LOCAL_SOONG_INSTALL_PAIRS",
+					fi.builtFile.String()+":"+filepath.Join(p.installDir.String(), fi.stem()))
 
 				// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar  Therefore
 				// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
@@ -471,6 +472,10 @@
 	inputApex android.Path
 }
 
+func (p *Prebuilt) InstallBypassMake() bool {
+	return true
+}
+
 type ApexFileProperties struct {
 	// the path to the prebuilt .apex file to import.
 	//
@@ -756,15 +761,15 @@
 	// Save the files that need to be made available to Make.
 	p.initApexFilesForAndroidMk(ctx)
 
-	if p.installable() {
-		ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
-	}
-
 	// in case that prebuilt_apex replaces source apex (using prefer: prop)
-	p.compatSymlinks = makeCompatSymlinks(p.BaseModuleName(), ctx)
+	p.compatSymlinks = makeCompatSymlinks(p.BaseModuleName(), ctx, true)
 	// or that prebuilt_apex overrides other apexes (using overrides: prop)
 	for _, overridden := range p.prebuiltCommonProperties.Overrides {
-		p.compatSymlinks = append(p.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
+		p.compatSymlinks = append(p.compatSymlinks, makeCompatSymlinks(overridden, ctx, true)...)
+	}
+
+	if p.installable() {
+		p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks.Paths()...)
 	}
 }
 
@@ -964,10 +969,10 @@
 	}
 
 	// in case that apex_set replaces source apex (using prefer: prop)
-	a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
+	a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, true)
 	// or that apex_set overrides other apexes (using overrides: prop)
 	for _, overridden := range a.prebuiltCommonProperties.Overrides {
-		a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
+		a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx, true)...)
 	}
 }
 
diff --git a/apex/vndk.go b/apex/vndk.go
index 75c0fb0..cf525a8 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -15,7 +15,6 @@
 package apex
 
 import (
-	"path/filepath"
 	"strings"
 
 	"android/soong/android"
@@ -96,11 +95,14 @@
 }
 
 // name is module.BaseModuleName() which is used as LOCAL_MODULE_NAME and also LOCAL_OVERRIDES_*
-func makeCompatSymlinks(name string, ctx android.ModuleContext) (symlinks []string) {
+func makeCompatSymlinks(name string, ctx android.ModuleContext, primaryApex bool) (symlinks android.InstallPaths) {
 	// small helper to add symlink commands
-	addSymlink := func(target, dir, linkName string) {
-		link := filepath.Join(dir, linkName)
-		symlinks = append(symlinks, "mkdir -p "+dir+" && rm -rf "+link+" && ln -sf "+target+" "+link)
+	addSymlink := func(target string, dir android.InstallPath, linkName string) {
+		if primaryApex {
+			symlinks = append(symlinks, ctx.InstallAbsoluteSymlink(dir, linkName, target))
+		} else {
+			symlinks = append(symlinks, dir.Join(ctx, linkName))
+		}
 	}
 
 	// TODO(b/142911355): [VNDK APEX] Fix hard-coded references to /system/lib/vndk
@@ -118,14 +120,15 @@
 		// the name of vndk apex is formatted "com.android.vndk.v" + version
 		apexName := vndkApexNamePrefix + vndkVersion
 		if ctx.Config().Android64() {
-			addSymlink("/apex/"+apexName+"/lib64", "$(TARGET_OUT)/lib64", "vndk-sp-"+vndkVersion)
-			addSymlink("/apex/"+apexName+"/lib64", "$(TARGET_OUT)/lib64", "vndk-"+vndkVersion)
+			dir := android.PathForModuleInPartitionInstall(ctx, "system", "lib64")
+			addSymlink("/apex/"+apexName+"/lib64", dir, "vndk-sp-"+vndkVersion)
+			addSymlink("/apex/"+apexName+"/lib64", dir, "vndk-"+vndkVersion)
 		}
 		if !ctx.Config().Android64() || ctx.DeviceConfig().DeviceSecondaryArch() != "" {
-			addSymlink("/apex/"+apexName+"/lib", "$(TARGET_OUT)/lib", "vndk-sp-"+vndkVersion)
-			addSymlink("/apex/"+apexName+"/lib", "$(TARGET_OUT)/lib", "vndk-"+vndkVersion)
+			dir := android.PathForModuleInPartitionInstall(ctx, "system", "lib")
+			addSymlink("/apex/"+apexName+"/lib", dir, "vndk-sp-"+vndkVersion)
+			addSymlink("/apex/"+apexName+"/lib", dir, "vndk-"+vndkVersion)
 		}
-		return
 	}
 
 	// http://b/121248172 - create a link from /system/usr/icu to
@@ -133,19 +136,25 @@
 	// A symlink can't overwrite a directory and the /system/usr/icu directory once
 	// existed so the required structure must be created whatever we find.
 	if name == "com.android.i18n" {
-		addSymlink("/apex/com.android.i18n/etc/icu", "$(TARGET_OUT)/usr", "icu")
-		return
+		dir := android.PathForModuleInPartitionInstall(ctx, "system", "usr")
+		addSymlink("/apex/com.android.i18n/etc/icu", dir, "icu")
 	}
 
 	// TODO(b/124106384): Clean up compat symlinks for ART binaries.
-	if name == "com.android.art" || strings.HasPrefix(name, "com.android.art.") {
-		addSymlink("/apex/com.android.art/bin/dalvikvm", "$(TARGET_OUT)/bin", "dalvikvm")
+	if name == "com.android.art" {
+		dir := android.PathForModuleInPartitionInstall(ctx, "system", "bin")
+		addSymlink("/apex/com.android.art/bin/dalvikvm", dir, "dalvikvm")
 		dex2oat := "dex2oat32"
 		if ctx.Config().Android64() {
 			dex2oat = "dex2oat64"
 		}
-		addSymlink("/apex/com.android.art/bin/"+dex2oat, "$(TARGET_OUT)/bin", "dex2oat")
-		return
+		addSymlink("/apex/com.android.art/bin/"+dex2oat, dir, "dex2oat")
+	} else if name == "com.android.art" || strings.HasPrefix(name, "com.android.art.") {
+		dir := android.PathForModuleInPartitionInstall(ctx, "system", "bin")
+		symlinks = append(symlinks,
+			dir.Join(ctx, "dalvikvm"),
+			dir.Join(ctx, "dex2oat"))
 	}
-	return
+
+	return symlinks
 }
diff --git a/bazel/properties.go b/bazel/properties.go
index a438481..b370bbf 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -24,23 +24,6 @@
 	"github.com/google/blueprint"
 )
 
-type BazelModuleProperties struct {
-	// The label of the Bazel target replacing this Soong module. When run in conversion mode, this
-	// will import the handcrafted build target into the autogenerated file. Note: this may result in
-	// a conflict due to duplicate targets if bp2build_available is also set.
-	Label *string
-
-	// If true, bp2build will generate the converted Bazel target for this module. Note: this may
-	// cause a conflict due to the duplicate targets if label is also set.
-	//
-	// This is a bool pointer to support tristates: true, false, not set.
-	//
-	// To opt-in a module, set bazel_module: { bp2build_available: true }
-	// To opt-out a module, set bazel_module: { bp2build_available: false }
-	// To defer the default setting for the directory, do not set the value.
-	Bp2build_available *bool
-}
-
 // BazelTargetModuleProperties contain properties and metadata used for
 // Blueprint to BUILD file conversion.
 type BazelTargetModuleProperties struct {
@@ -417,6 +400,12 @@
 	// This mode facilitates use of attribute defaults: an empty list should
 	// override the default.
 	ForceSpecifyEmptyList bool
+
+	// If true, signal the intent to the code generator to emit all select keys,
+	// even if the Includes list for that key is empty. This mode facilitates
+	// specific select statements where an empty list for a non-default select
+	// key has a meaning.
+	EmitEmptyList bool
 }
 
 type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 1250e92..9b66354 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -18,6 +18,7 @@
     ],
     deps: [
         "soong-android",
+        "soong-android-soongconfig",
         "soong-apex",
         "soong-bazel",
         "soong-cc",
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index aa1cf70..10ee582 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -532,8 +532,8 @@
 
 // prettyPrint a property value into the equivalent Starlark representation
 // recursively.
-func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
-	if isZero(propertyValue) {
+func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) (string, error) {
+	if !emitZeroValues && isZero(propertyValue) {
 		// A property value being set or unset actually matters -- Soong does set default
 		// values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
 		// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
@@ -556,7 +556,7 @@
 	case reflect.Int, reflect.Uint, reflect.Int64:
 		ret = fmt.Sprintf("%v", propertyValue.Interface())
 	case reflect.Ptr:
-		return prettyPrint(propertyValue.Elem(), indent)
+		return prettyPrint(propertyValue.Elem(), indent, emitZeroValues)
 	case reflect.Slice:
 		if propertyValue.Len() == 0 {
 			return "[]", nil
@@ -565,7 +565,7 @@
 		if propertyValue.Len() == 1 {
 			// Single-line list for list with only 1 element
 			ret += "["
-			indexedValue, err := prettyPrint(propertyValue.Index(0), indent)
+			indexedValue, err := prettyPrint(propertyValue.Index(0), indent, emitZeroValues)
 			if err != nil {
 				return "", err
 			}
@@ -575,7 +575,7 @@
 			// otherwise, use a multiline list.
 			ret += "[\n"
 			for i := 0; i < propertyValue.Len(); i++ {
-				indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
+				indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1, emitZeroValues)
 				if err != nil {
 					return "", err
 				}
@@ -660,7 +660,7 @@
 		}
 
 		propertyName := proptools.PropertyNameForField(field.Name)
-		prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
+		prettyPrintedValue, err := prettyPrint(fieldValue, indent+1, false)
 		if err != nil {
 			panic(
 				fmt.Errorf(
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 0bcf91d..c953259 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -95,7 +95,7 @@
 				continue
 			}
 			selectKey := axis.SelectKey(config)
-			if use, value := labelListSelectValue(selectKey, labels); use {
+			if use, value := labelListSelectValue(selectKey, labels, list.EmitEmptyList); use {
 				archSelects[selectKey] = value
 			}
 		}
@@ -107,8 +107,8 @@
 	return value, ret
 }
 
-func labelListSelectValue(selectKey string, list bazel.LabelList) (bool, reflect.Value) {
-	if selectKey == bazel.ConditionsDefaultSelectKey || len(list.Includes) > 0 {
+func labelListSelectValue(selectKey string, list bazel.LabelList, emitEmptyList bool) (bool, reflect.Value) {
+	if selectKey == bazel.ConditionsDefaultSelectKey || emitEmptyList || len(list.Includes) > 0 {
 		return true, reflect.ValueOf(list.Includes)
 	} else if len(list.Excludes) > 0 {
 		// if there is still an excludes -- we need to have an empty list for this select & use the
@@ -129,6 +129,7 @@
 	var value reflect.Value
 	var configurableAttrs []selects
 	var defaultSelectValue *string
+	var emitZeroValues bool
 	// If true, print the default attribute value, even if the attribute is zero.
 	shouldPrintDefault := false
 	switch list := v.(type) {
@@ -137,6 +138,7 @@
 		defaultSelectValue = &emptyBazelList
 	case bazel.LabelListAttribute:
 		value, configurableAttrs = getLabelListValues(list)
+		emitZeroValues = list.EmitEmptyList
 		defaultSelectValue = &emptyBazelList
 		if list.ForceSpecifyEmptyList && (!value.IsNil() || list.HasConfigurableValues()) {
 			shouldPrintDefault = true
@@ -154,7 +156,7 @@
 	var err error
 	ret := ""
 	if value.Kind() != reflect.Invalid {
-		s, err := prettyPrint(value, indent)
+		s, err := prettyPrint(value, indent, false) // never emit zero values for the base value
 		if err != nil {
 			return ret, err
 		}
@@ -163,7 +165,7 @@
 	}
 	// Convenience function to append selects components to an attribute value.
 	appendSelects := func(selectsData selects, defaultValue *string, s string) (string, error) {
-		selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent)
+		selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent, emitZeroValues)
 		if err != nil {
 			return "", err
 		}
@@ -190,7 +192,7 @@
 
 // prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way
 // to construct a select map for any kind of attribute type.
-func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *string, indent int) (string, error) {
+func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *string, indent int, emitZeroValues bool) (string, error) {
 	if selectMap == nil {
 		return "", nil
 	}
@@ -202,11 +204,11 @@
 			continue
 		}
 		value := selectMap[selectKey]
-		if isZero(value) {
+		if isZero(value) && !emitZeroValues {
 			// Ignore zero values to not generate empty lists.
 			continue
 		}
-		s, err := prettyPrintSelectEntry(value, selectKey, indent)
+		s, err := prettyPrintSelectEntry(value, selectKey, indent, emitZeroValues)
 		if err != nil {
 			return "", err
 		}
@@ -227,7 +229,7 @@
 	ret += selects
 
 	// Handle the default condition
-	s, err := prettyPrintSelectEntry(selectMap[bazel.ConditionsDefaultSelectKey], bazel.ConditionsDefaultSelectKey, indent)
+	s, err := prettyPrintSelectEntry(selectMap[bazel.ConditionsDefaultSelectKey], bazel.ConditionsDefaultSelectKey, indent, emitZeroValues)
 	if err != nil {
 		return "", err
 	}
@@ -249,9 +251,9 @@
 
 // prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
 // with a provided key.
-func prettyPrintSelectEntry(value reflect.Value, key string, indent int) (string, error) {
+func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) {
 	s := makeIndent(indent + 1)
-	v, err := prettyPrint(value, indent+1)
+	v, err := prettyPrint(value, indent+1, emitZeroValues)
 	if err != nil {
 		return "", err
 	}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 944cb83..81a4b26 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -25,6 +25,8 @@
 
 	files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n")))
 
+	files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
+
 	return files
 }
 
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index d09238a..3e6d9e6 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -82,7 +82,8 @@
 }
 
 func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
-	files := CreateSoongInjectionFiles(android.Config{}, CodegenMetrics{})
+	testConfig := android.TestConfig("", make(map[string]string), "", make(map[string][]byte))
+	files := CreateSoongInjectionFiles(testConfig, CodegenMetrics{})
 
 	expectedFilePaths := []bazelFilepath{
 		{
@@ -97,6 +98,10 @@
 			dir:      "metrics",
 			basename: "converted_modules.txt",
 		},
+		{
+			dir:      "product_config",
+			basename: "soong_config_variables.bzl",
+		},
 	}
 
 	if len(files) != len(expectedFilePaths) {
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index 2e6f54c..d21db04 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -32,6 +32,8 @@
 	ctx.RegisterModuleType("soong_config_module_type", android.SoongConfigModuleTypeFactory)
 	ctx.RegisterModuleType("soong_config_string_variable", android.SoongConfigStringVariableDummyFactory)
 	ctx.RegisterModuleType("soong_config_bool_variable", android.SoongConfigBoolVariableDummyFactory)
+
+	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
 }
 
 func TestSoongConfigModuleType(t *testing.T) {
@@ -42,7 +44,6 @@
 	config_namespace: "acme",
 	bool_variables: ["feature1"],
 	properties: ["cflags"],
-	bazel_module: { bp2build_available: true },
 }
 
 custom_cc_library_static {
@@ -83,7 +84,6 @@
 	config_namespace: "acme",
 	bool_variables: ["feature1"],
 	properties: ["cflags"],
-	bazel_module: { bp2build_available: true },
 }
 `
 	bp := `
@@ -138,7 +138,6 @@
 	config_namespace: "acme",
 	variables: ["board"],
 	properties: ["cflags"],
-	bazel_module: { bp2build_available: true },
 }
 
 custom_cc_library_static {
@@ -199,7 +198,6 @@
 	config_namespace: "acme",
 	variables: ["feature1", "feature2", "board"],
 	properties: ["cflags"],
-	bazel_module: { bp2build_available: true },
 }
 
 custom_cc_library_static {
@@ -269,7 +267,6 @@
 	config_namespace: "acme",
 	variables: ["board"],
 	properties: ["cflags", "static_libs"],
-	bazel_module: { bp2build_available: true },
 }
 
 custom_cc_library_static {
@@ -324,3 +321,527 @@
     local_includes = ["."],
 )`}})
 }
+
+func TestSoongConfigModuleType_Defaults_SingleNamespace(t *testing.T) {
+	bp := `
+soong_config_module_type {
+	name: "vendor_foo_cc_defaults",
+	module_type: "cc_defaults",
+	config_namespace: "vendor_foo",
+	bool_variables: ["feature"],
+	properties: ["cflags", "cppflags"],
+}
+
+vendor_foo_cc_defaults {
+	name: "foo_defaults_1",
+	soong_config_variables: {
+		feature: {
+			cflags: ["-cflag_feature_1"],
+			conditions_default: {
+				cflags: ["-cflag_default_1"],
+			},
+		},
+	},
+}
+
+vendor_foo_cc_defaults {
+	name: "foo_defaults_2",
+	defaults: ["foo_defaults_1"],
+	soong_config_variables: {
+		feature: {
+			cflags: ["-cflag_feature_2"],
+			conditions_default: {
+				cflags: ["-cflag_default_2"],
+			},
+		},
+	},
+}
+
+cc_library_static {
+	name: "lib",
+	defaults: ["foo_defaults_2"],
+	bazel_module: { bp2build_available: true },
+}
+`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                        "soong config variables - defaults with a single namespace",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint:                          bp,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "lib",
+    copts = select({
+        "//build/bazel/product_variables:vendor_foo__feature__enabled": [
+            "-cflag_feature_2",
+            "-cflag_feature_1",
+        ],
+        "//conditions:default": [
+            "-cflag_default_2",
+            "-cflag_default_1",
+        ],
+    }),
+    local_includes = ["."],
+)`}})
+}
+
+func TestSoongConfigModuleType_MultipleDefaults_SingleNamespace(t *testing.T) {
+	bp := `
+soong_config_module_type {
+	name: "foo_cc_defaults",
+	module_type: "cc_defaults",
+	config_namespace: "acme",
+	bool_variables: ["feature"],
+	properties: ["cflags"],
+}
+
+soong_config_module_type {
+	name: "bar_cc_defaults",
+	module_type: "cc_defaults",
+	config_namespace: "acme",
+	bool_variables: ["feature"],
+	properties: ["cflags", "asflags"],
+}
+
+foo_cc_defaults {
+	name: "foo_defaults",
+	soong_config_variables: {
+		feature: {
+			cflags: ["-cflag_foo"],
+			conditions_default: {
+				cflags: ["-cflag_default_foo"],
+			},
+		},
+	},
+}
+
+bar_cc_defaults {
+	name: "bar_defaults",
+	srcs: ["file.S"],
+	soong_config_variables: {
+		feature: {
+			cflags: ["-cflag_bar"],
+			asflags: ["-asflag_bar"],
+			conditions_default: {
+				asflags: ["-asflag_default_bar"],
+				cflags: ["-cflag_default_bar"],
+			},
+		},
+	},
+}
+
+cc_library_static {
+	name: "lib",
+	defaults: ["foo_defaults", "bar_defaults"],
+	bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+	name: "lib2",
+	defaults: ["bar_defaults", "foo_defaults"],
+	bazel_module: { bp2build_available: true },
+}
+`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                        "soong config variables - multiple defaults with a single namespace",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint:                          bp,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "lib",
+    asflags = select({
+        "//build/bazel/product_variables:acme__feature__enabled": ["-asflag_bar"],
+        "//conditions:default": ["-asflag_default_bar"],
+    }),
+    copts = select({
+        "//build/bazel/product_variables:acme__feature__enabled": [
+            "-cflag_foo",
+            "-cflag_bar",
+        ],
+        "//conditions:default": [
+            "-cflag_default_foo",
+            "-cflag_default_bar",
+        ],
+    }),
+    local_includes = ["."],
+    srcs_as = ["file.S"],
+)`,
+			`cc_library_static(
+    name = "lib2",
+    asflags = select({
+        "//build/bazel/product_variables:acme__feature__enabled": ["-asflag_bar"],
+        "//conditions:default": ["-asflag_default_bar"],
+    }),
+    copts = select({
+        "//build/bazel/product_variables:acme__feature__enabled": [
+            "-cflag_bar",
+            "-cflag_foo",
+        ],
+        "//conditions:default": [
+            "-cflag_default_bar",
+            "-cflag_default_foo",
+        ],
+    }),
+    local_includes = ["."],
+    srcs_as = ["file.S"],
+)`}})
+}
+
+func TestSoongConfigModuleType_Defaults_MultipleNamespaces(t *testing.T) {
+	bp := `
+soong_config_module_type {
+	name: "vendor_foo_cc_defaults",
+	module_type: "cc_defaults",
+	config_namespace: "vendor_foo",
+	bool_variables: ["feature"],
+	properties: ["cflags"],
+}
+
+soong_config_module_type {
+	name: "vendor_bar_cc_defaults",
+	module_type: "cc_defaults",
+	config_namespace: "vendor_bar",
+	bool_variables: ["feature"],
+	properties: ["cflags"],
+}
+
+soong_config_module_type {
+	name: "vendor_qux_cc_defaults",
+	module_type: "cc_defaults",
+	config_namespace: "vendor_qux",
+	bool_variables: ["feature"],
+	properties: ["cflags"],
+}
+
+vendor_foo_cc_defaults {
+	name: "foo_defaults",
+	soong_config_variables: {
+		feature: {
+			cflags: ["-DVENDOR_FOO_FEATURE"],
+			conditions_default: {
+				cflags: ["-DVENDOR_FOO_DEFAULT"],
+			},
+		},
+	},
+}
+
+vendor_bar_cc_defaults {
+	name: "bar_defaults",
+	soong_config_variables: {
+		feature: {
+			cflags: ["-DVENDOR_BAR_FEATURE"],
+			conditions_default: {
+				cflags: ["-DVENDOR_BAR_DEFAULT"],
+			},
+		},
+	},
+}
+
+vendor_qux_cc_defaults {
+	name: "qux_defaults",
+	defaults: ["bar_defaults"],
+	soong_config_variables: {
+		feature: {
+			cflags: ["-DVENDOR_QUX_FEATURE"],
+			conditions_default: {
+				cflags: ["-DVENDOR_QUX_DEFAULT"],
+			},
+		},
+	},
+}
+
+cc_library_static {
+	name: "lib",
+	defaults: ["foo_defaults", "qux_defaults"],
+	bazel_module: { bp2build_available: true },
+}
+`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                        "soong config variables - defaults with multiple namespaces",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint:                          bp,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "lib",
+    copts = select({
+        "//build/bazel/product_variables:vendor_bar__feature__enabled": ["-DVENDOR_BAR_FEATURE"],
+        "//conditions:default": ["-DVENDOR_BAR_DEFAULT"],
+    }) + select({
+        "//build/bazel/product_variables:vendor_foo__feature__enabled": ["-DVENDOR_FOO_FEATURE"],
+        "//conditions:default": ["-DVENDOR_FOO_DEFAULT"],
+    }) + select({
+        "//build/bazel/product_variables:vendor_qux__feature__enabled": ["-DVENDOR_QUX_FEATURE"],
+        "//conditions:default": ["-DVENDOR_QUX_DEFAULT"],
+    }),
+    local_includes = ["."],
+)`}})
+}
+
+func TestSoongConfigModuleType_Defaults(t *testing.T) {
+	bp := `
+soong_config_string_variable {
+    name: "library_linking_strategy",
+    values: [
+        "prefer_static",
+    ],
+}
+
+soong_config_module_type {
+    name: "library_linking_strategy_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "ANDROID",
+    variables: ["library_linking_strategy"],
+    properties: [
+        "shared_libs",
+        "static_libs",
+    ],
+}
+
+library_linking_strategy_cc_defaults {
+    name: "library_linking_strategy_lib_a_defaults",
+    soong_config_variables: {
+        library_linking_strategy: {
+            prefer_static: {
+                static_libs: [
+                    "lib_a",
+                ],
+            },
+            conditions_default: {
+                shared_libs: [
+                    "lib_a",
+                ],
+            },
+        },
+    },
+}
+
+library_linking_strategy_cc_defaults {
+    name: "library_linking_strategy_merged_defaults",
+    defaults: ["library_linking_strategy_lib_a_defaults"],
+    soong_config_variables: {
+        library_linking_strategy: {
+            prefer_static: {
+                static_libs: [
+                    "lib_b",
+                ],
+            },
+            conditions_default: {
+                shared_libs: [
+                    "lib_b",
+                ],
+            },
+        },
+    },
+}
+
+cc_binary {
+    name: "library_linking_strategy_sample_binary",
+    srcs: ["library_linking_strategy.cc"],
+    defaults: ["library_linking_strategy_merged_defaults"],
+}`
+
+	otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_default", bazel_module: { bp2build_available: false } }
+`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                        "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:                "cc_binary",
+		moduleTypeUnderTestFactory:         cc.BinaryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.BinaryBp2build,
+		blueprint:                          bp,
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": otherDeps,
+		},
+		expectedBazelTargets: []string{`cc_binary(
+    name = "library_linking_strategy_sample_binary",
+    deps = select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [
+            "//foo/bar:lib_b_bp2build_cc_library_static",
+            "//foo/bar:lib_a_bp2build_cc_library_static",
+        ],
+        "//conditions:default": [],
+    }),
+    dynamic_deps = select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//conditions:default": [
+            "//foo/bar:lib_b",
+            "//foo/bar:lib_a",
+        ],
+    }),
+    local_includes = ["."],
+    srcs = ["library_linking_strategy.cc"],
+)`}})
+}
+
+func TestSoongConfigModuleType_Defaults_Another(t *testing.T) {
+	bp := `
+soong_config_string_variable {
+    name: "library_linking_strategy",
+    values: [
+        "prefer_static",
+    ],
+}
+
+soong_config_module_type {
+    name: "library_linking_strategy_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "ANDROID",
+    variables: ["library_linking_strategy"],
+    properties: [
+        "shared_libs",
+        "static_libs",
+    ],
+}
+
+library_linking_strategy_cc_defaults {
+    name: "library_linking_strategy_sample_defaults",
+    soong_config_variables: {
+        library_linking_strategy: {
+            prefer_static: {
+                static_libs: [
+                    "lib_a",
+                    "lib_b",
+                ],
+            },
+            conditions_default: {
+                shared_libs: [
+                    "lib_a",
+                    "lib_b",
+                ],
+            },
+        },
+    },
+}
+
+cc_binary {
+    name: "library_linking_strategy_sample_binary",
+    srcs: ["library_linking_strategy.cc"],
+    defaults: ["library_linking_strategy_sample_defaults"],
+}`
+
+	otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
+`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                        "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:                "cc_binary",
+		moduleTypeUnderTestFactory:         cc.BinaryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.BinaryBp2build,
+		blueprint:                          bp,
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": otherDeps,
+		},
+		expectedBazelTargets: []string{`cc_binary(
+    name = "library_linking_strategy_sample_binary",
+    deps = select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [
+            "//foo/bar:lib_a_bp2build_cc_library_static",
+            "//foo/bar:lib_b_bp2build_cc_library_static",
+        ],
+        "//conditions:default": [],
+    }),
+    dynamic_deps = select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//conditions:default": [
+            "//foo/bar:lib_a",
+            "//foo/bar:lib_b",
+        ],
+    }),
+    local_includes = ["."],
+    srcs = ["library_linking_strategy.cc"],
+)`}})
+}
+
+func TestSoongConfigModuleType_Defaults_UnusedProps(t *testing.T) {
+	bp := `
+soong_config_string_variable {
+    name: "alphabet",
+    values: [
+        "a",
+        "b",
+        "c", // unused
+    ],
+}
+
+soong_config_module_type {
+    name: "alphabet_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "ANDROID",
+    variables: ["alphabet"],
+    properties: [
+        "cflags", // unused
+        "shared_libs",
+        "static_libs",
+    ],
+}
+
+alphabet_cc_defaults {
+    name: "alphabet_sample_cc_defaults",
+    soong_config_variables: {
+        alphabet: {
+            a: {
+                shared_libs: [
+                    "lib_a",
+                ],
+            },
+            b: {
+                shared_libs: [
+                    "lib_b",
+                ],
+            },
+            conditions_default: {
+                static_libs: [
+                    "lib_default",
+                ],
+            },
+        },
+    },
+}
+
+cc_binary {
+    name: "alphabet_binary",
+    srcs: ["main.cc"],
+    defaults: ["alphabet_sample_cc_defaults"],
+}`
+
+	otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_default", bazel_module: { bp2build_available: false } }
+`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                        "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:                "cc_binary",
+		moduleTypeUnderTestFactory:         cc.BinaryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.BinaryBp2build,
+		blueprint:                          bp,
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": otherDeps,
+		},
+		expectedBazelTargets: []string{`cc_binary(
+    name = "alphabet_binary",
+    deps = select({
+        "//build/bazel/product_variables:android__alphabet__a": [],
+        "//build/bazel/product_variables:android__alphabet__b": [],
+        "//conditions:default": ["//foo/bar:lib_default_bp2build_cc_library_static"],
+    }),
+    dynamic_deps = select({
+        "//build/bazel/product_variables:android__alphabet__a": ["//foo/bar:lib_a"],
+        "//build/bazel/product_variables:android__alphabet__b": ["//foo/bar:lib_b"],
+        "//conditions:default": [],
+    }),
+    local_includes = ["."],
+    srcs = ["main.cc"],
+)`}})
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index dde8dd4..d949436 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -611,6 +611,7 @@
 				ctx.ModuleErrorf("Could not convert product variable %s property", dep.excludesField)
 			}
 
+			dep.attribute.EmitEmptyList = productConfigProp.AlwaysEmit()
 			dep.attribute.SetSelectValue(
 				productConfigProp.ConfigurationAxis(),
 				productConfigProp.SelectKey(),
diff --git a/cc/config/global.go b/cc/config/global.go
index 7abd2fc..0b229be 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -270,8 +270,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r433403b"
-	ClangDefaultShortVersion = "13.0.3"
+	ClangDefaultVersion      = "clang-r437112"
+	ClangDefaultShortVersion = "14.0.0"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 9577a8c..492cd98 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -101,6 +101,9 @@
 	"android.hardware.weaver-unstable-ndk_platform",
 	"android.system.suspend-V1-ndk",
 	"android.system.keystore2-V1-ndk",
+	"android.se.omapi-V1-ndk_platform",
+	"android.se.omapi-ndk_platform",
+	"android.se.omapi-unstable-ndk_platform",
 	"android.hardware.wifi.hostapd-V1-ndk",
 	"android.hardware.wifi.hostapd-V1-ndk_platform",
 	"android.hardware.wifi.supplicant-V1-ndk",
diff --git a/java/sdk_library.go b/java/sdk_library.go
index d7f14d6..3065d57 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -21,6 +21,7 @@
 	"reflect"
 	"regexp"
 	"sort"
+	"strconv"
 	"strings"
 	"sync"
 
@@ -32,25 +33,7 @@
 )
 
 const (
-	sdkXmlFileSuffix    = ".xml"
-	permissionsTemplate = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n` +
-		`<!-- Copyright (C) 2018 The Android Open Source Project\n` +
-		`\n` +
-		`    Licensed under the Apache License, Version 2.0 (the \"License\");\n` +
-		`    you may not use this file except in compliance with the License.\n` +
-		`    You may obtain a copy of the License at\n` +
-		`\n` +
-		`        http://www.apache.org/licenses/LICENSE-2.0\n` +
-		`\n` +
-		`    Unless required by applicable law or agreed to in writing, software\n` +
-		`    distributed under the License is distributed on an \"AS IS\" BASIS,\n` +
-		`    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n` +
-		`    See the License for the specific language governing permissions and\n` +
-		`    limitations under the License.\n` +
-		`-->\n` +
-		`<permissions>\n` +
-		`    <library name=\"%s\" file=\"%s\"/>\n` +
-		`</permissions>\n`
+	sdkXmlFileSuffix = ".xml"
 )
 
 // A tag to associated a dependency with a specific api scope.
@@ -637,6 +620,33 @@
 
 	// Files containing information about supported java doc tags.
 	Doctag_files []string `android:"path"`
+
+	// Signals that this shared library is part of the bootclasspath starting
+	// on the version indicated in this attribute.
+	//
+	// This will make platforms at this level and above to ignore
+	// <uses-library> tags with this library name because the library is already
+	// available
+	On_bootclasspath_since *string
+
+	// Signals that this shared library was part of the bootclasspath before
+	// (but not including) the version indicated in this attribute.
+	//
+	// The system will automatically add a <uses-library> tag with this library to
+	// apps that target any SDK less than the version indicated in this attribute.
+	On_bootclasspath_before *string
+
+	// Indicates that PackageManager should ignore this shared library if the
+	// platform is below the version indicated in this attribute.
+	//
+	// This means that the device won't recognise this library as installed.
+	Min_device_sdk *string
+
+	// Indicates that PackageManager should ignore this shared library if the
+	// platform is above the version indicated in this attribute.
+	//
+	// This means that the device won't recognise this library as installed.
+	Max_device_sdk *string
 }
 
 // commonSdkLibraryAndImportModule defines the interface that must be provided by a module that
@@ -1580,14 +1590,29 @@
 
 // Creates the xml file that publicizes the runtime library
 func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
+	moduleMinApiLevel := module.Library.MinSdkVersion(mctx).ApiLevel
+	var moduleMinApiLevelStr = moduleMinApiLevel.String()
+	if moduleMinApiLevel == android.NoneApiLevel {
+		moduleMinApiLevelStr = "current"
+	}
 	props := struct {
-		Name           *string
-		Lib_name       *string
-		Apex_available []string
+		Name                      *string
+		Lib_name                  *string
+		Apex_available            []string
+		On_bootclasspath_since    *string
+		On_bootclasspath_before   *string
+		Min_device_sdk            *string
+		Max_device_sdk            *string
+		Sdk_library_min_api_level *string
 	}{
-		Name:           proptools.StringPtr(module.xmlPermissionsModuleName()),
-		Lib_name:       proptools.StringPtr(module.BaseModuleName()),
-		Apex_available: module.ApexProperties.Apex_available,
+		Name:                      proptools.StringPtr(module.xmlPermissionsModuleName()),
+		Lib_name:                  proptools.StringPtr(module.BaseModuleName()),
+		Apex_available:            module.ApexProperties.Apex_available,
+		On_bootclasspath_since:    module.commonSdkLibraryProperties.On_bootclasspath_since,
+		On_bootclasspath_before:   module.commonSdkLibraryProperties.On_bootclasspath_before,
+		Min_device_sdk:            module.commonSdkLibraryProperties.Min_device_sdk,
+		Max_device_sdk:            module.commonSdkLibraryProperties.Max_device_sdk,
+		Sdk_library_min_api_level: &moduleMinApiLevelStr,
 	}
 
 	mctx.CreateModule(sdkLibraryXmlFactory, &props)
@@ -2383,6 +2408,38 @@
 type sdkLibraryXmlProperties struct {
 	// canonical name of the lib
 	Lib_name *string
+
+	// Signals that this shared library is part of the bootclasspath starting
+	// on the version indicated in this attribute.
+	//
+	// This will make platforms at this level and above to ignore
+	// <uses-library> tags with this library name because the library is already
+	// available
+	On_bootclasspath_since *string
+
+	// Signals that this shared library was part of the bootclasspath before
+	// (but not including) the version indicated in this attribute.
+	//
+	// The system will automatically add a <uses-library> tag with this library to
+	// apps that target any SDK less than the version indicated in this attribute.
+	On_bootclasspath_before *string
+
+	// Indicates that PackageManager should ignore this shared library if the
+	// platform is below the version indicated in this attribute.
+	//
+	// This means that the device won't recognise this library as installed.
+	Min_device_sdk *string
+
+	// Indicates that PackageManager should ignore this shared library if the
+	// platform is above the version indicated in this attribute.
+	//
+	// This means that the device won't recognise this library as installed.
+	Max_device_sdk *string
+
+	// The SdkLibrary's min api level as a string
+	//
+	// This value comes from the ApiLevel of the MinSdkVersion property.
+	Sdk_library_min_api_level *string
 }
 
 // java_sdk_library_xml builds the permission xml file for a java_sdk_library.
@@ -2459,11 +2516,81 @@
 	return "/" + partition + "/framework/" + implName + ".jar"
 }
 
+func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string {
+	if value == nil {
+		return ""
+	}
+	apiLevel, err := android.ApiLevelFromUser(ctx, *value)
+	if err != nil {
+		// attributes in bp files have underscores but in the xml have dashes.
+		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
+		return ""
+	}
+	intStr := strconv.Itoa(apiLevel.FinalOrPreviewInt())
+	return formattedOptionalAttribute(attrName, &intStr)
+}
+
+// formats an attribute for the xml permissions file if the value is not null
+// returns empty string otherwise
+func formattedOptionalAttribute(attrName string, value *string) string {
+	if value == nil {
+		return ""
+	}
+	return fmt.Sprintf(`        %s=\"%s\"\n`, attrName, *value)
+}
+
+func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string {
+	libName := proptools.String(module.properties.Lib_name)
+	libNameAttr := formattedOptionalAttribute("name", &libName)
+	filePath := module.implPath(ctx)
+	filePathAttr := formattedOptionalAttribute("file", &filePath)
+	implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since)
+	implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before)
+	minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk)
+	maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk)
+	// <library> is understood in all android versions whereas <updatable-library> is only understood from API T (and ignored before that).
+	// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the updatable-library to make sure this library is not loaded before T
+	var libraryTag string
+	if module.properties.Min_device_sdk != nil {
+		libraryTag = `    <updatable-library\n`
+	} else {
+		libraryTag = `    <library\n`
+	}
+
+	return strings.Join([]string{
+		`<?xml version=\"1.0\" encoding=\"utf-8\"?>\n`,
+		`<!-- Copyright (C) 2018 The Android Open Source Project\n`,
+		`\n`,
+		`    Licensed under the Apache License, Version 2.0 (the \"License\");\n`,
+		`    you may not use this file except in compliance with the License.\n`,
+		`    You may obtain a copy of the License at\n`,
+		`\n`,
+		`        http://www.apache.org/licenses/LICENSE-2.0\n`,
+		`\n`,
+		`    Unless required by applicable law or agreed to in writing, software\n`,
+		`    distributed under the License is distributed on an \"AS IS\" BASIS,\n`,
+		`    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n`,
+		`    See the License for the specific language governing permissions and\n`,
+		`    limitations under the License.\n`,
+		`-->\n`,
+		`<permissions>\n`,
+		libraryTag,
+		libNameAttr,
+		filePathAttr,
+		implicitFromAttr,
+		implicitUntilAttr,
+		minSdkAttr,
+		maxSdkAttr,
+		`    />\n`,
+		`</permissions>\n`}, "")
+}
+
 func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	module.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
 
 	libName := proptools.String(module.properties.Lib_name)
-	xmlContent := fmt.Sprintf(permissionsTemplate, libName, module.implPath(ctx))
+	module.selfValidate(ctx)
+	xmlContent := module.permissionsContents(ctx)
 
 	module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
 	rule := android.NewRuleBuilder(pctx, ctx)
@@ -2496,6 +2623,81 @@
 	}}
 }
 
+func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) {
+	module.validateAtLeastTAttributes(ctx)
+	module.validateMinAndMaxDeviceSdk(ctx)
+	module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx)
+	module.validateOnBootclasspathBeforeRequirements(ctx)
+}
+
+func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) {
+	t := android.ApiLevelOrPanic(ctx, "Tiramisu")
+	module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk")
+	module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk")
+	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before")
+	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since")
+}
+
+func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) {
+	if attr != nil {
+		if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil {
+			// we will inform the user of invalid inputs when we try to write the
+			// permissions xml file so we don't need to do it here
+			if t.GreaterThan(level) {
+				ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T")
+			}
+		}
+	}
+}
+
+func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) {
+	if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil {
+		min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
+		max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
+		if minErr == nil && maxErr == nil {
+			// we will inform the user of invalid inputs when we try to write the
+			// permissions xml file so we don't need to do it here
+			if min.GreaterThan(max) {
+				ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk")
+			}
+		}
+	}
+}
+
+func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) {
+	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
+	if module.properties.Min_device_sdk != nil {
+		api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
+		if err == nil {
+			if moduleMinApi.GreaterThan(api) {
+				ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
+			}
+		}
+	}
+	if module.properties.Max_device_sdk != nil {
+		api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
+		if err == nil {
+			if moduleMinApi.GreaterThan(api) {
+				ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
+			}
+		}
+	}
+}
+
+func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) {
+	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
+	if module.properties.On_bootclasspath_before != nil {
+		t := android.ApiLevelOrPanic(ctx, "Tiramisu")
+		// if we use the attribute, then we need to do this validation
+		if moduleMinApi.LessThan(t) {
+			// if minAPi is < T, then we need to have min_device_sdk (which only accepts T+)
+			if module.properties.Min_device_sdk == nil {
+				ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T")
+			}
+		}
+	}
+}
+
 type sdkLibrarySdkMemberType struct {
 	android.SdkMemberTypeBase
 }
@@ -2547,6 +2749,33 @@
 	Doctag_paths android.Paths
 
 	Permitted_packages []string
+
+	// Signals that this shared library is part of the bootclasspath starting
+	// on the version indicated in this attribute.
+	//
+	// This will make platforms at this level and above to ignore
+	// <uses-library> tags with this library name because the library is already
+	// available
+	On_bootclasspath_since *string
+
+	// Signals that this shared library was part of the bootclasspath before
+	// (but not including) the version indicated in this attribute.
+	//
+	// The system will automatically add a <uses-library> tag with this library to
+	// apps that target any SDK less than the version indicated in this attribute.
+	On_bootclasspath_before *string
+
+	// Indicates that PackageManager should ignore this shared library if the
+	// platform is below the version indicated in this attribute.
+	//
+	// This means that the device won't recognise this library as installed.
+	Min_device_sdk *string
+
+	// Indicates that PackageManager should ignore this shared library if the
+	// platform is above the version indicated in this attribute.
+	//
+	// This means that the device won't recognise this library as installed.
+	Max_device_sdk *string
 }
 
 type scopeProperties struct {
@@ -2593,6 +2822,10 @@
 	s.Compile_dex = sdk.dexProperties.Compile_dex
 	s.Doctag_paths = sdk.doctagPaths
 	s.Permitted_packages = sdk.PermittedPackagesForUpdatableBootJars()
+	s.On_bootclasspath_since = sdk.commonSdkLibraryProperties.On_bootclasspath_since
+	s.On_bootclasspath_before = sdk.commonSdkLibraryProperties.On_bootclasspath_before
+	s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk
+	s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk
 }
 
 func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index be23536..2271573 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -15,12 +15,13 @@
 package java
 
 import (
-	"android/soong/android"
 	"fmt"
 	"path/filepath"
 	"regexp"
 	"testing"
 
+	"android/soong/android"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -107,7 +108,7 @@
 			libs: ["foo"],
 			sdk_version: "module_30",
 		}
-		`)
+	`)
 
 	// check the existence of the internal modules
 	foo := result.ModuleForTests("foo", "android_common")
@@ -162,6 +163,185 @@
 	}
 }
 
+func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithPrebuiltApis(map[string][]string{
+			"28": {"foo"},
+			"29": {"foo"},
+			"30": {"foo", "fooUpdatable", "fooUpdatableErr"},
+		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W"}
+		}),
+	).RunTestWithBp(t,
+		`
+		java_sdk_library {
+			name: "fooUpdatable",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			on_bootclasspath_since: "U",
+			on_bootclasspath_before: "V",
+			min_device_sdk: "W",
+			max_device_sdk: "current",
+			min_sdk_version: "S",
+		}
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+		}
+`)
+	// test that updatability attributes are passed on correctly
+	fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Rule("java_sdk_xml")
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"9001\"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"9002\"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"9003\"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"10000\"`)
+
+	// double check that updatability attributes are not written if they don't exist in the bp file
+	// the permissions file for the foo library defined above
+	fooPermissions := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml")
+	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-since`)
+	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-before`)
+	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `min-device-sdk`)
+	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `max-device-sdk`)
+}
+
+func TestJavaSdkLibrary_UpdatableLibrary_Validation_ValidVersion(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithPrebuiltApis(map[string][]string{
+			"30": {"fooUpdatable", "fooUpdatableErr"},
+		}),
+	).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(
+		[]string{
+			`on_bootclasspath_since: "aaa" could not be parsed as an integer and is not a recognized codename`,
+			`on_bootclasspath_before: "bbc" could not be parsed as an integer and is not a recognized codename`,
+			`min_device_sdk: "ccc" could not be parsed as an integer and is not a recognized codename`,
+			`max_device_sdk: "ddd" could not be parsed as an integer and is not a recognized codename`,
+		})).RunTestWithBp(t,
+		`
+	java_sdk_library {
+			name: "fooUpdatableErr",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			on_bootclasspath_since: "aaa",
+			on_bootclasspath_before: "bbc",
+			min_device_sdk: "ccc",
+			max_device_sdk: "ddd",
+		}
+`)
+}
+
+func TestJavaSdkLibrary_UpdatableLibrary_Validation_AtLeastTAttributes(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithPrebuiltApis(map[string][]string{
+			"28": {"foo"},
+		}),
+	).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(
+		[]string{
+			"on_bootclasspath_since: Attribute value needs to be at least T",
+			"on_bootclasspath_before: Attribute value needs to be at least T",
+			"min_device_sdk: Attribute value needs to be at least T",
+			"max_device_sdk: Attribute value needs to be at least T",
+		},
+	)).RunTestWithBp(t,
+		`
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			on_bootclasspath_since: "S",
+			on_bootclasspath_before: "S",
+			min_device_sdk: "S",
+			max_device_sdk: "S",
+			min_sdk_version: "S",
+		}
+`)
+}
+
+func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdk(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithPrebuiltApis(map[string][]string{
+			"28": {"foo"},
+		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V"}
+		}),
+	).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(
+		[]string{
+			"min_device_sdk can't be greater than max_device_sdk",
+		},
+	)).RunTestWithBp(t,
+		`
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			min_device_sdk: "V",
+			max_device_sdk: "U",
+			min_sdk_version: "S",
+		}
+`)
+}
+
+func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdkAndModuleMinSdk(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithPrebuiltApis(map[string][]string{
+			"28": {"foo"},
+		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V"}
+		}),
+	).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(
+		[]string{
+			regexp.QuoteMeta("min_device_sdk: Can't be less than module's min sdk (V)"),
+			regexp.QuoteMeta("max_device_sdk: Can't be less than module's min sdk (V)"),
+		},
+	)).RunTestWithBp(t,
+		`
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			min_device_sdk: "U",
+			max_device_sdk: "U",
+			min_sdk_version: "V",
+		}
+`)
+}
+
+func TestJavaSdkLibrary_UpdatableLibrary_usesNewTag(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithPrebuiltApis(map[string][]string{
+			"30": {"foo"},
+		}),
+	).RunTestWithBp(t,
+		`
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			min_device_sdk: "Tiramisu",
+			min_sdk_version: "S",
+		}
+`)
+	// test that updatability attributes are passed on correctly
+	fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml")
+	android.AssertStringDoesContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<updatable-library`)
+	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<library`)
+}
+
 func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index c55300e..4b9779c 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -1711,7 +1711,7 @@
 	fmt.Fprintf(&buf, "load(%q, input_variables_init = \"init\")\n", inputVariablesUri)
 	fmt.Fprintf(&buf, "globals, cfg, globals_base = %s(init, input_variables_init)\n", cfnBoardMain)
 	fmt.Fprintf(&buf, "# TODO: Some product config variables need to be printed, but most are readonly so we can't just print cfg here.\n")
-	fmt.Fprintf(&buf, "%s(globals, globals_base)\n", cfnPrintGlobals)
+	fmt.Fprintf(&buf, "%s((globals, cfg, globals_base))\n", cfnPrintVars)
 	return buf.String()
 }
 
diff --git a/rust/config/global.go b/rust/config/global.go
index ebddb75..d3826ac 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.56.1"
+	RustDefaultVersion = "1.56.1p1"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/scripts/reverse-deps.sh b/scripts/reverse-deps.sh
index 02b7dcb..410f5c0 100755
--- a/scripts/reverse-deps.sh
+++ b/scripts/reverse-deps.sh
@@ -67,6 +67,9 @@
       $0 ~ /^\S\S*:$/ {
         inoutput = 0
       }
+      $1 == "validations:" {
+        inoutput = 0
+      }
       inoutput != 0 {
         print gensub(/^\s*/, "", "g")" "depth
       }
diff --git a/scripts/transitive-deps.sh b/scripts/transitive-deps.sh
index ba36ba4..cf9fb94 100755
--- a/scripts/transitive-deps.sh
+++ b/scripts/transitive-deps.sh
@@ -100,7 +100,7 @@
         currFileName = ""
         currExt = ""
       }
-      $1 == "outputs:" {
+      $1 == "outputs:" || $1 == "validations:" {
         ininput = 0
       }
       ininput == 0 && $0 ~ /^\S\S*:$/ {
diff --git a/scripts/unpack-prebuilt-apex.sh b/scripts/unpack-prebuilt-apex.sh
index 1acdeb5..f34a480 100755
--- a/scripts/unpack-prebuilt-apex.sh
+++ b/scripts/unpack-prebuilt-apex.sh
@@ -29,8 +29,6 @@
 shift 4
 REQUIRED_PATHS=$@
 
-set -x 1
-
 rm -fr $OUTPUT_DIR
 mkdir -p $OUTPUT_DIR
 
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index a376e06..b19949a 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -7,4 +7,4 @@
 "$TOP/build/soong/tests/mixed_mode_test.sh"
 "$TOP/build/soong/tests/bp2build_bazel_test.sh"
 "$TOP/build/soong/tests/soong_test.sh"
-"$TOP/build/bazel/ci/rbc_product_config.sh" aosp_arm64-userdebug
+"$TOP/build/bazel/ci/rbc_regression_test.sh" aosp_arm64-userdebug