Merge "Add Mac 11.1 SDK to the allowlist"
diff --git a/Android.bp b/Android.bp
index 866ed25..1012dba 100644
--- a/Android.bp
+++ b/Android.bp
@@ -222,3 +222,8 @@
     srcs: [":linker"],
     out: ["linker.flags"],
 }
+
+// Instantiate the dex_bootjars singleton module.
+dex_bootjars {
+    name: "dex_bootjars",
+}
diff --git a/README.md b/README.md
index f1857f8..b7e93f4 100644
--- a/README.md
+++ b/README.md
@@ -430,14 +430,24 @@
 
 soong_config_string_variable {
     name: "board",
-    values: ["soc_a", "soc_b"],
+    values: ["soc_a", "soc_b", "soc_c"],
 }
 ```
 
 This example describes a new `acme_cc_defaults` module type that extends the
 `cc_defaults` module type, with three additional conditionals based on
 variables `board`, `feature` and `width`, which can affect properties `cflags`
-and `srcs`.
+and `srcs`. Additionally, each conditional will contain a `conditions_default`
+property can affect `cflags` and `srcs` in the following conditions:
+
+* bool variable (e.g. `feature`): the variable is unspecified or not set to a true value
+* value variable (e.g. `width`): the variable is unspecified
+* string variable (e.g. `board`): the variable is unspecified or the variable is set to a string unused in the
+given module. For example, with `board`, if the `board`
+conditional contains the properties `soc_a` and `conditions_default`, when
+board=soc_b, the `cflags` and `srcs` values under `conditions_default` will be
+used. To specify that no properties should be amended for `soc_b`, you can set
+`soc_b: {},`.
 
 The values of the variables can be set from a product's `BoardConfig.mk` file:
 ```
@@ -445,6 +455,7 @@
 SOONG_CONFIG_acme += \
     board \
     feature \
+    width \
 
 SOONG_CONFIG_acme_board := soc_a
 SOONG_CONFIG_acme_feature := true
@@ -473,12 +484,21 @@
             soc_b: {
                 cflags: ["-DSOC_B"],
             },
+            conditions_default: {
+                cflags: ["-DSOC_DEFAULT"],
+            },
         },
         feature: {
             cflags: ["-DFEATURE"],
+            conditions_default: {
+                cflags: ["-DFEATURE_DEFAULT"],
+            },
         },
         width: {
             cflags: ["-DWIDTH=%s"],
+            conditions_default: {
+                cflags: ["-DWIDTH=DEFAULT"],
+            },
         },
     },
 }
@@ -490,8 +510,37 @@
 }
 ```
 
-With the `BoardConfig.mk` snippet above, libacme_foo would build with
-cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
+With the `BoardConfig.mk` snippet above, `libacme_foo` would build with
+`cflags: "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200"`.
+
+Alternatively, with `DefaultBoardConfig.mk`:
+
+```
+SOONG_CONFIG_NAMESPACES += acme
+SOONG_CONFIG_acme += \
+    board \
+    feature \
+    width \
+
+SOONG_CONFIG_acme_feature := false
+```
+
+then `libacme_foo` would build with `cflags: "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT"`.
+
+Alternatively, with `DefaultBoardConfig.mk`:
+
+```
+SOONG_CONFIG_NAMESPACES += acme
+SOONG_CONFIG_acme += \
+    board \
+    feature \
+    width \
+
+SOONG_CONFIG_acme_board := soc_c
+```
+
+then `libacme_foo` would build with `cflags: "-DGENERIC -DSOC_DEFAULT
+-DFEATURE_DEFAULT -DSIZE=DEFAULT"`.
 
 `soong_config_module_type` modules will work best when used to wrap defaults
 modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 619cf86..289e910 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -51,6 +51,16 @@
 // variables from another Android.bp file.  The imported module type will exist for all
 // modules after the import in the Android.bp file.
 //
+// Each soong_config_variable supports an additional value `conditions_default`. The properties
+// specified in `conditions_default` will only be used under the following conditions:
+//   bool variable: the variable is unspecified or not set to a true value
+//   value variable: the variable is unspecified
+//   string variable: the variable is unspecified or the variable is set to a string unused in the
+//                    given module. For example, string variable `test` takes values: "a" and "b",
+//                    if the module contains a property `a` and `conditions_default`, when test=b,
+//                    the properties under `conditions_default` will be used. To specify that no
+//                    properties should be amended for `b`, you can set `b: {},`.
+//
 // For example, an Android.bp file could have:
 //
 //     soong_config_module_type_import {
@@ -69,12 +79,21 @@
 //                 soc_b: {
 //                     cflags: ["-DSOC_B"],
 //                 },
+//                 conditions_default: {
+//                     cflags: ["-DSOC_DEFAULT"],
+//                 },
 //             },
 //             feature: {
 //                 cflags: ["-DFEATURE"],
+//                 conditions_default: {
+//                     cflags: ["-DFEATURE_DEFAULT"],
+//                 },
 //             },
 //             width: {
 //                 cflags: ["-DWIDTH=%s"],
+//                 conditions_default: {
+//                     cflags: ["-DWIDTH=DEFAULT"],
+//                 },
 //             },
 //         },
 //     }
@@ -99,7 +118,7 @@
 //
 //     soong_config_string_variable {
 //         name: "board",
-//         values: ["soc_a", "soc_b"],
+//         values: ["soc_a", "soc_b", "soc_c"],
 //     }
 //
 // If an acme BoardConfig.mk file contained:
@@ -114,6 +133,31 @@
 //     SOONG_CONFIG_acme_width := 200
 //
 // Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
+//
+// Alternatively, if acme BoardConfig.mk file contained:
+//
+//     SOONG_CONFIG_NAMESPACES += acme
+//     SOONG_CONFIG_acme += \
+//         board \
+//         feature \
+//
+//     SOONG_CONFIG_acme_feature := false
+//
+// Then libacme_foo would build with cflags:
+//   "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
+//
+// Similarly, if acme BoardConfig.mk file contained:
+//
+//     SOONG_CONFIG_NAMESPACES += acme
+//     SOONG_CONFIG_acme += \
+//         board \
+//         feature \
+//
+//     SOONG_CONFIG_acme_board := soc_c
+//
+// Then libacme_foo would build with cflags:
+//   "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
+
 func soongConfigModuleTypeImportFactory() Module {
 	module := &soongConfigModuleTypeImport{}
 
@@ -148,6 +192,16 @@
 // in an Android.bp file, and can be imported into other Android.bp files using
 // soong_config_module_type_import.
 //
+// Each soong_config_variable supports an additional value `conditions_default`. The properties
+// specified in `conditions_default` will only be used under the following conditions:
+//   bool variable: the variable is unspecified or not set to a true value
+//   value variable: the variable is unspecified
+//   string variable: the variable is unspecified or the variable is set to a string unused in the
+//                    given module. For example, string variable `test` takes values: "a" and "b",
+//                    if the module contains a property `a` and `conditions_default`, when test=b,
+//                    the properties under `conditions_default` will be used. To specify that no
+//                    properties should be amended for `b`, you can set `b: {},`.
+//
 // For example, an Android.bp file could have:
 //
 //     soong_config_module_type {
@@ -176,12 +230,21 @@
 //                 soc_b: {
 //                     cflags: ["-DSOC_B"],
 //                 },
+//                 conditions_default: {
+//                     cflags: ["-DSOC_DEFAULT"],
+//                 },
 //             },
 //             feature: {
 //                 cflags: ["-DFEATURE"],
+//                 conditions_default: {
+//                     cflags: ["-DFEATURE_DEFAULT"],
+//                 },
 //             },
 //             width: {
 //	               cflags: ["-DWIDTH=%s"],
+//                 conditions_default: {
+//                     cflags: ["-DWIDTH=DEFAULT"],
+//                 },
 //             },
 //         },
 //     }
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index b1810b3..45463fd 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -60,15 +60,20 @@
 			name: "acme_test",
 			module_type: "test",
 			config_namespace: "acme",
-			variables: ["board", "feature1", "FEATURE3"],
-			bool_variables: ["feature2"],
-			value_variables: ["size"],
+			variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
+			bool_variables: ["feature2", "unused_feature"],
+			value_variables: ["size", "unused_size"],
 			properties: ["cflags", "srcs", "defaults"],
 		}
 
 		soong_config_string_variable {
 			name: "board",
-			values: ["soc_a", "soc_b"],
+			values: ["soc_a", "soc_b", "soc_c", "soc_d"],
+		}
+
+		soong_config_string_variable {
+			name: "unused_string_var",
+			values: ["a", "b"],
 		}
 
 		soong_config_bool_variable {
@@ -105,15 +110,28 @@
 					soc_b: {
 						cflags: ["-DSOC_B"],
 					},
+					soc_c: {},
+					conditions_default: {
+						cflags: ["-DSOC_CONDITIONS_DEFAULT"],
+					},
 				},
 				size: {
 					cflags: ["-DSIZE=%s"],
+					conditions_default: {
+						cflags: ["-DSIZE=CONDITIONS_DEFAULT"],
+					},
 				},
 				feature1: {
+					  conditions_default: {
+						  cflags: ["-DF1_CONDITIONS_DEFAULT"],
+					  },
 					cflags: ["-DFEATURE1"],
 				},
 				feature2: {
 					cflags: ["-DFEATURE2"],
+					 conditions_default: {
+						 cflags: ["-DF2_CONDITIONS_DEFAULT"],
+					 },
 				},
 				FEATURE3: {
 					cflags: ["-DFEATURE3"],
@@ -145,6 +163,7 @@
 						cflags: ["-DSOC_B"],
 						defaults: ["foo_defaults_b"],
 					},
+					soc_c: {},
 				},
 				size: {
 					cflags: ["-DSIZE=%s"],
@@ -163,43 +182,120 @@
     `
 
 	run := func(t *testing.T, bp string, fs map[string][]byte) {
-		config := TestConfig(buildDir, nil, bp, fs)
+		testCases := []struct {
+			name                     string
+			config                   Config
+			fooExpectedFlags         []string
+			fooDefaultsExpectedFlags []string
+		}{
+			{
+				name: "withValues",
+				config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
+					"acme": map[string]string{
+						"board":    "soc_a",
+						"size":     "42",
+						"feature1": "true",
+						"feature2": "false",
+						// FEATURE3 unset
+						"unused_feature":    "true", // unused
+						"unused_size":       "1",    // unused
+						"unused_string_var": "a",    // unused
+					},
+				}),
+				fooExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+					"-DF2_CONDITIONS_DEFAULT",
+					"-DSIZE=42",
+					"-DSOC_A",
+					"-DFEATURE1",
+				},
+				fooDefaultsExpectedFlags: []string{
+					"DEFAULT_A",
+					"DEFAULT",
+					"-DGENERIC",
+					"-DSIZE=42",
+					"-DSOC_A",
+					"-DFEATURE1",
+				},
+			},
+			{
+				name: "empty_prop_for_string_var",
+				config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
+					"acme": map[string]string{"board": "soc_c"}}),
+				fooExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+					"-DF2_CONDITIONS_DEFAULT",
+					"-DSIZE=CONDITIONS_DEFAULT",
+					"-DF1_CONDITIONS_DEFAULT",
+				},
+				fooDefaultsExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+				},
+			},
+			{
+				name: "unused_string_var",
+				config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
+					"acme": map[string]string{"board": "soc_d"}}),
+				fooExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+					"-DF2_CONDITIONS_DEFAULT",
+					"-DSIZE=CONDITIONS_DEFAULT",
+					"-DSOC_CONDITIONS_DEFAULT", // foo does not contain a prop "soc_d", so we use the default
+					"-DF1_CONDITIONS_DEFAULT",
+				},
+				fooDefaultsExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+				},
+			},
 
-		config.TestProductVariables.VendorVars = map[string]map[string]string{
-			"acme": map[string]string{
-				"board":    "soc_a",
-				"size":     "42",
-				"feature1": "true",
-				"feature2": "false",
-				// FEATURE3 unset
+			{
+				name:   "conditions_default",
+				config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{}),
+				fooExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+					"-DF2_CONDITIONS_DEFAULT",
+					"-DSIZE=CONDITIONS_DEFAULT",
+					"-DSOC_CONDITIONS_DEFAULT",
+					"-DF1_CONDITIONS_DEFAULT",
+				},
+				fooDefaultsExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+				},
 			},
 		}
 
-		ctx := NewTestContext(config)
-		ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
-		ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
-		ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
-		ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
-		ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
-		ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
-		ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
-		ctx.Register()
+		for _, tc := range testCases {
+			ctx := NewTestContext(tc.config)
+			ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
+			ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
+			ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
+			ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
+			ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
+			ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
+			ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+			ctx.Register()
 
-		_, errs := ctx.ParseBlueprintsFiles("Android.bp")
-		FailIfErrored(t, errs)
-		_, errs = ctx.PrepareBuildActions(config)
-		FailIfErrored(t, errs)
+			_, errs := ctx.ParseBlueprintsFiles("Android.bp")
+			FailIfErrored(t, errs)
+			_, errs = ctx.PrepareBuildActions(tc.config)
+			FailIfErrored(t, errs)
 
-		basicCFlags := []string{"DEFAULT", "-DGENERIC", "-DSIZE=42", "-DSOC_A", "-DFEATURE1"}
+			foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
+			if g, w := foo.props.Cflags, tc.fooExpectedFlags; !reflect.DeepEqual(g, w) {
+				t.Errorf("%s: wanted foo cflags %q, got %q", tc.name, w, g)
+			}
 
-		foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
-		if g, w := foo.props.Cflags, basicCFlags; !reflect.DeepEqual(g, w) {
-			t.Errorf("wanted foo cflags %q, got %q", w, g)
-		}
-
-		fooDefaults := ctx.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
-		if g, w := fooDefaults.props.Cflags, append([]string{"DEFAULT_A"}, basicCFlags...); !reflect.DeepEqual(g, w) {
-			t.Errorf("wanted foo_with_defaults cflags %q, got %q", w, g)
+			fooDefaults := ctx.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
+			if g, w := fooDefaults.props.Cflags, tc.fooDefaultsExpectedFlags; !reflect.DeepEqual(g, w) {
+				t.Errorf("%s: wanted foo_with_defaults cflags %q, got %q", tc.name, w, g)
+			}
 		}
 
 	}
@@ -214,3 +310,11 @@
 		})
 	})
 }
+
+func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
+	config := TestConfig(buildDir, nil, bp, fs)
+
+	config.TestProductVariables.VendorVars = vendorVars
+
+	return config
+}
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index df912e6..6bb68eb 100644
--- a/android/soongconfig/Android.bp
+++ b/android/soongconfig/Android.bp
@@ -10,4 +10,7 @@
         "config.go",
         "modules.go",
     ],
+    testSrcs: [
+        "modules_test.go",
+    ],
 }
diff --git a/android/soongconfig/config.go b/android/soongconfig/config.go
index 39a776c..c72da2f 100644
--- a/android/soongconfig/config.go
+++ b/android/soongconfig/config.go
@@ -14,7 +14,10 @@
 
 package soongconfig
 
-import "strings"
+import (
+	"fmt"
+	"strings"
+)
 
 type SoongConfig interface {
 	// Bool interprets the variable named `name` as a boolean, returning true if, after
@@ -31,7 +34,16 @@
 }
 
 func Config(vars map[string]string) SoongConfig {
-	return soongConfig(vars)
+	configVars := make(map[string]string)
+	if len(vars) > 0 {
+		for k, v := range vars {
+			configVars[k] = v
+		}
+		if _, exists := configVars[conditionsDefault]; exists {
+			panic(fmt.Sprintf("%q is a reserved soong config variable name", conditionsDefault))
+		}
+	}
+	return soongConfig(configVars)
 }
 
 type soongConfig map[string]string
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 9f3f804..c62e76d 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -26,6 +26,8 @@
 	"github.com/google/blueprint/proptools"
 )
 
+const conditionsDefault = "conditions_default"
+
 var soongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
 
 // loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file.  It caches the
@@ -145,32 +147,10 @@
 		return errs
 	}
 
-	mt := &ModuleType{
-		affectableProperties: props.Properties,
-		ConfigNamespace:      props.Config_namespace,
-		BaseModuleType:       props.Module_type,
-		variableNames:        props.Variables,
-	}
-	v.ModuleTypes[props.Name] = mt
-
-	for _, name := range props.Bool_variables {
-		if name == "" {
-			return []error{fmt.Errorf("bool_variable name must not be blank")}
-		}
-
-		mt.Variables = append(mt.Variables, newBoolVariable(name))
-	}
-
-	for _, name := range props.Value_variables {
-		if name == "" {
-			return []error{fmt.Errorf("value_variables entry must not be blank")}
-		}
-
-		mt.Variables = append(mt.Variables, &valueVariable{
-			baseVariable: baseVariable{
-				variable: name,
-			},
-		})
+	if mt, errs := newModuleType(props); len(errs) > 0 {
+		return errs
+	} else {
+		v.ModuleTypes[props.Name] = mt
 	}
 
 	return nil
@@ -196,6 +176,12 @@
 		return []error{fmt.Errorf("values property must be set")}
 	}
 
+	for _, name := range stringProps.Values {
+		if err := checkVariableName(name); err != nil {
+			return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
+		}
+	}
+
 	v.variables[base.variable] = &stringVariable{
 		baseVariable: base,
 		values:       CanonicalizeToProperties(stringProps.Values),
@@ -417,8 +403,7 @@
 // PropertiesToApply returns the applicable properties from a ModuleType that should be applied
 // based on SoongConfig values.
 // Expects that props contains a struct field with name soong_config_variables. The fields within
-// soong_config_variables are expected to be in the same order as moduleType.Variables. In general,
-// props should be generated via CreateProperties.
+// soong_config_variables are expected to be in the same order as moduleType.Variables.
 func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
 	var ret []interface{}
 	props = props.Elem().FieldByName(soongConfigProperty)
@@ -441,6 +426,46 @@
 	variableNames        []string
 }
 
+func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
+	mt := &ModuleType{
+		affectableProperties: props.Properties,
+		ConfigNamespace:      props.Config_namespace,
+		BaseModuleType:       props.Module_type,
+		variableNames:        props.Variables,
+	}
+
+	for _, name := range props.Bool_variables {
+		if err := checkVariableName(name); err != nil {
+			return nil, []error{fmt.Errorf("bool_variables %s", err)}
+		}
+
+		mt.Variables = append(mt.Variables, newBoolVariable(name))
+	}
+
+	for _, name := range props.Value_variables {
+		if err := checkVariableName(name); err != nil {
+			return nil, []error{fmt.Errorf("value_variables %s", err)}
+		}
+
+		mt.Variables = append(mt.Variables, &valueVariable{
+			baseVariable: baseVariable{
+				variable: name,
+			},
+		})
+	}
+
+	return mt, nil
+}
+
+func checkVariableName(name string) error {
+	if name == "" {
+		return fmt.Errorf("name must not be blank")
+	} else if name == conditionsDefault {
+		return fmt.Errorf("%q is reserved", conditionsDefault)
+	}
+	return nil
+}
+
 type soongConfigVariable interface {
 	// variableProperty returns the name of the variable.
 	variableProperty() string
@@ -474,7 +499,10 @@
 func (s *stringVariable) variableValuesType() reflect.Type {
 	var fields []reflect.StructField
 
-	for _, v := range s.values {
+	var values []string
+	values = append(values, s.values...)
+	values = append(values, conditionsDefault)
+	for _, v := range values {
 		fields = append(fields, reflect.StructField{
 			Name: proptools.FieldNameForProperty(v),
 			Type: emptyInterfaceType,
@@ -484,26 +512,36 @@
 	return reflect.StructOf(fields)
 }
 
+// initializeProperties initializes properties to zero value of typ for supported values and a final
+// conditions default field.
 func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
 	for i := range s.values {
 		v.Field(i).Set(reflect.Zero(typ))
 	}
+	v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
 }
 
+// Extracts an interface from values containing the properties to apply based on config.
+// If config does not match a value with a non-nil property set, the default value will be returned.
 func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
 	for j, v := range s.values {
-		if config.String(s.variable) == v {
-			return values.Field(j).Interface(), nil
+		f := values.Field(j)
+		if config.String(s.variable) == v && !f.Elem().IsNil() {
+			return f.Interface(), nil
 		}
 	}
-
-	return nil, nil
+	// if we have reached this point, we have checked all valid values of string and either:
+	//   * the value was not set
+	//   * the value was set but that value was not specified in the Android.bp file
+	return values.Field(len(s.values)).Interface(), nil
 }
 
+// Struct to allow conditions set based on a boolean variable
 type boolVariable struct {
 	baseVariable
 }
 
+// newBoolVariable constructs a boolVariable with the given name
 func newBoolVariable(name string) *boolVariable {
 	return &boolVariable{
 		baseVariable{
@@ -516,18 +554,82 @@
 	return emptyInterfaceType
 }
 
+// initializeProperties initializes a property to zero value of typ with an additional conditions
+// default field.
 func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
-	v.Set(reflect.Zero(typ))
+	initializePropertiesWithDefault(v, typ)
 }
 
-func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
-	if config.Bool(b.variable) {
-		return values.Interface(), nil
+// initializePropertiesWithDefault, initialize with zero value,  v to contain a field for each field
+// in typ, with an additional field for defaults of type typ. This should be used to initialize
+// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
+// one variable and a default.
+func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
+	sTyp := typ.Elem()
+	var fields []reflect.StructField
+	for i := 0; i < sTyp.NumField(); i++ {
+		fields = append(fields, sTyp.Field(i))
 	}
 
+	// create conditions_default field
+	nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
+	fields = append(fields, reflect.StructField{
+		Name: nestedFieldName,
+		Type: typ,
+	})
+
+	newTyp := reflect.PtrTo(reflect.StructOf(fields))
+	v.Set(reflect.Zero(newTyp))
+}
+
+// conditionsDefaultField extracts the conditions_default field from v. This is always the final
+// field if initialized with initializePropertiesWithDefault.
+func conditionsDefaultField(v reflect.Value) reflect.Value {
+	return v.Field(v.NumField() - 1)
+}
+
+// removeDefault removes the conditions_default field from values while retaining values from all
+// other fields. This allows
+func removeDefault(values reflect.Value) reflect.Value {
+	v := values.Elem().Elem()
+	s := conditionsDefaultField(v)
+	// if conditions_default field was not set, there will be no issues extending properties.
+	if !s.IsValid() {
+		return v
+	}
+
+	// If conditions_default field was set, it has the correct type for our property. Create a new
+	// reflect.Value of the conditions_default type and copy all fields (except for
+	// conditions_default) based on values to the result.
+	res := reflect.New(s.Type().Elem())
+	for i := 0; i < res.Type().Elem().NumField(); i++ {
+		val := v.Field(i)
+		res.Elem().Field(i).Set(val)
+	}
+
+	return res
+}
+
+// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
+// the module. If the value was not set, conditions_default interface will be returned; otherwise,
+// the interface in values, without conditions_default will be returned.
+func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
+	// If this variable was not referenced in the module, there are no properties to apply.
+	if values.Elem().IsZero() {
+		return nil, nil
+	}
+	if config.Bool(b.variable) {
+		values = removeDefault(values)
+		return values.Interface(), nil
+	}
+	v := values.Elem().Elem()
+	if f := conditionsDefaultField(v); f.IsValid() {
+		return f.Interface(), nil
+	}
 	return nil, nil
 }
 
+// Struct to allow conditions set based on a value variable, supporting string substitution.
 type valueVariable struct {
 	baseVariable
 }
@@ -536,17 +638,28 @@
 	return emptyInterfaceType
 }
 
+// initializeProperties initializes a property to zero value of typ with an additional conditions
+// default field.
 func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
-	v.Set(reflect.Zero(typ))
+	initializePropertiesWithDefault(v, typ)
 }
 
+// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
+// the module. If the variable was not set, conditions_default interface will be returned;
+// otherwise, the interface in values, without conditions_default will be returned with all
+// appropriate string substitutions based on variable being set.
 func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
-	if !config.IsSet(s.variable) || !values.IsValid() {
+	// If this variable was not referenced in the module, there are no properties to apply.
+	if !values.IsValid() || values.Elem().IsZero() {
 		return nil, nil
 	}
+	if !config.IsSet(s.variable) {
+		return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
+	}
 	configValue := config.String(s.variable)
 
-	propStruct := values.Elem().Elem()
+	values = removeDefault(values)
+	propStruct := values.Elem()
 	if !propStruct.IsValid() {
 		return nil, nil
 	}
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index fb0e189..b824c78 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -254,67 +254,75 @@
 	A *string
 	B bool
 }
-type soongConfigVariables struct {
-	Bool_var       properties
-	Other_bool_var properties
+
+type boolVarProps struct {
+	A                  *string
+	B                  bool
+	Conditions_default *properties
 }
 
-type soongConfigProps struct {
-	Soong_config_variables soongConfigVariables
+type soongConfigVars struct {
+	Bool_var interface{}
 }
 
 func Test_PropertiesToApply(t *testing.T) {
-
-	mt := &ModuleType{
-		BaseModuleType:  "foo",
-		ConfigNamespace: "bar",
-		Variables: []soongConfigVariable{
-			newBoolVariable("bool_var"),
-			newBoolVariable("other_bool_var"),
-		},
-		affectableProperties: []string{
-			"a",
-			"b",
-		},
+	mt, _ := newModuleType(&ModuleTypeProperties{
+		Module_type:      "foo",
+		Config_namespace: "bar",
+		Bool_variables:   []string{"bool_var"},
+		Properties:       []string{"a", "b"},
+	})
+	boolVarPositive := &properties{
+		A: proptools.StringPtr("A"),
+		B: true,
 	}
-	props := soongConfigProps{
-		Soong_config_variables: soongConfigVariables{
-			Bool_var: properties{
-				A: proptools.StringPtr("a"),
-				B: true,
-			},
-			Other_bool_var: properties{
-				A: proptools.StringPtr("other"),
-				B: false,
+	conditionsDefault := &properties{
+		A: proptools.StringPtr("default"),
+		B: false,
+	}
+	actualProps := &struct {
+		Soong_config_variables soongConfigVars
+	}{
+		Soong_config_variables: soongConfigVars{
+			Bool_var: &boolVarProps{
+				A:                  boolVarPositive.A,
+				B:                  boolVarPositive.B,
+				Conditions_default: conditionsDefault,
 			},
 		},
 	}
+	props := reflect.ValueOf(actualProps)
 
 	testCases := []struct {
+		name      string
 		config    SoongConfig
 		wantProps []interface{}
 	}{
 		{
-			config: Config(map[string]string{}),
+			name:      "no_vendor_config",
+			config:    Config(map[string]string{}),
+			wantProps: []interface{}{conditionsDefault},
 		},
 		{
+			name:      "vendor_config_false",
+			config:    Config(map[string]string{"bool_var": "n"}),
+			wantProps: []interface{}{conditionsDefault},
+		},
+		{
+			name:      "bool_var_true",
 			config:    Config(map[string]string{"bool_var": "y"}),
-			wantProps: []interface{}{props.Soong_config_variables.Bool_var},
-		},
-		{
-			config:    Config(map[string]string{"other_bool_var": "y"}),
-			wantProps: []interface{}{props.Soong_config_variables.Other_bool_var},
+			wantProps: []interface{}{boolVarPositive},
 		},
 	}
 
 	for _, tc := range testCases {
-		gotProps, err := PropertiesToApply(mt, reflect.ValueOf(&props), tc.config)
+		gotProps, err := PropertiesToApply(mt, props, tc.config)
 		if err != nil {
-			t.Errorf("Unexpected error in PropertiesToApply: %s", err)
+			t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err)
 		}
 
 		if !reflect.DeepEqual(gotProps, tc.wantProps) {
-			t.Errorf("Expected %s, got %s", tc.wantProps, gotProps)
+			t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps)
 		}
 	}
 }
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
index a5cfb83..c4ea381 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -171,7 +171,10 @@
 ExtServices-core(minSdkVersion:current)
 flatbuffer_headers(minSdkVersion:(no version))
 fmtlib(minSdkVersion:29)
+framework-permission(minSdkVersion:30)
 framework-permission(minSdkVersion:current)
+framework-permission-s(minSdkVersion:30)
+framework-permission-s-shared(minSdkVersion:30)
 framework-sdkextensions(minSdkVersion:30)
 framework-sdkextensions(minSdkVersion:current)
 framework-statsd(minSdkVersion:current)
@@ -456,6 +459,7 @@
 mediaswcodec(minSdkVersion:29)
 metrics-constants-protos(minSdkVersion:29)
 modules-utils-build(minSdkVersion:29)
+modules-utils-os(minSdkVersion:30)
 ndk_crtbegin_so.19(minSdkVersion:(no version))
 ndk_crtbegin_so.21(minSdkVersion:(no version))
 ndk_crtbegin_so.27(minSdkVersion:(no version))
@@ -573,7 +577,9 @@
 prebuilt_test_framework-sdkextensions(minSdkVersion:(no version))
 server_configurable_flags(minSdkVersion:29)
 service-media-s(minSdkVersion:29)
+service-permission(minSdkVersion:30)
 service-permission(minSdkVersion:current)
+service-permission-shared(minSdkVersion:30)
 service-statsd(minSdkVersion:current)
 SettingsLibActionBarShadow(minSdkVersion:21)
 SettingsLibAppPreference(minSdkVersion:21)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index d288414..b1e8480 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -248,6 +248,7 @@
 
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	rust.RegisterRequiredBuildComponentsForTest(ctx)
+	java.RegisterRequiredBuildComponentsForTest(ctx)
 
 	ctx.RegisterModuleType("cc_test", cc.TestFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
@@ -256,14 +257,6 @@
 	ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory)
 	ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	java.RegisterJavaBuildComponents(ctx)
-	java.RegisterSystemModulesBuildComponents(ctx)
-	java.RegisterAppBuildComponents(ctx)
-	java.RegisterAppImportBuildComponents(ctx)
-	java.RegisterAppSetBuildComponents(ctx)
-	java.RegisterRuntimeResourceOverlayBuildComponents(ctx)
-	java.RegisterSdkLibraryBuildComponents(ctx)
-	java.RegisterPrebuiltApisBuildComponents(ctx)
 	ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
 	ctx.RegisterModuleType("bpf", bpf.BpfFactory)
 
@@ -5957,17 +5950,13 @@
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	android.RegisterPrebuiltMutators(ctx)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	java.RegisterJavaBuildComponents(ctx)
-	java.RegisterSystemModulesBuildComponents(ctx)
-	java.RegisterAppBuildComponents(ctx)
-	java.RegisterDexpreoptBootJarsComponents(ctx)
+	java.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
 
 	ctx.Register()
 
-	_ = dexpreopt.GlobalSoongConfigForTests(config)
 	dexpreopt.RegisterToolModulesForTest(ctx)
 	pathCtx := android.PathContextForTesting(config)
 	dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
@@ -6164,9 +6153,7 @@
 	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	java.RegisterJavaBuildComponents(ctx)
-	java.RegisterSystemModulesBuildComponents(ctx)
-	java.RegisterDexpreoptBootJarsComponents(ctx)
+	java.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 286bb53..107ae7d 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -35,6 +35,7 @@
 	"android.hardware.security.sharedsecret-ndk_platform",
 	"android.hardware.security.sharedsecret-unstable-ndk_platform",
 	"android.hardware.vibrator-ndk_platform",
+	"android.hardware.weaver-unstable-ndk_platform",
 	"android.system.keystore2-unstable-ndk_platform",
 	"libbinder",
 	"libcrypto",
diff --git a/cc/library.go b/cc/library.go
index 29a3c69..af9aaca 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -449,39 +449,24 @@
 			}
 			continue
 		}
-		glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
-		if err != nil {
-			ctx.ModuleErrorf("glob failed: %#v", err)
-			return
+		exts := headerExts
+		// Glob all files under this special directory, because of C++ headers.
+		if strings.HasPrefix(dir, "external/libcxx/include") {
+			exts = []string{""}
 		}
-		isLibcxx := strings.HasPrefix(dir, "external/libcxx/include")
-		j := 0
-		for i, header := range glob {
-			if isLibcxx {
-				// Glob all files under this special directory, because of C++ headers with no
-				// extension.
-				if !strings.HasSuffix(header, "/") {
+		for _, ext := range exts {
+			glob, err := ctx.GlobWithDeps(dir+"/**/*"+ext, nil)
+			if err != nil {
+				ctx.ModuleErrorf("glob failed: %#v", err)
+				return
+			}
+			for _, header := range glob {
+				if strings.HasSuffix(header, "/") {
 					continue
 				}
-			} else {
-				// Filter out only the files with extensions that are headers.
-				found := false
-				for _, ext := range headerExts {
-					if strings.HasSuffix(header, ext) {
-						found = true
-						break
-					}
-				}
-				if !found {
-					continue
-				}
+				ret = append(ret, android.PathForSource(ctx, header))
 			}
-			if i != j {
-				glob[j] = glob[i]
-			}
-			j++
 		}
-		glob = glob[:j]
 	}
 
 	// Collect generated headers
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index feddfc0..867ece6 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -363,13 +363,6 @@
 // createGlobalSoongConfig creates a GlobalSoongConfig from the current context.
 // Should not be used in dexpreopt_gen.
 func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
-	if ctx.Config().TestProductVariables != nil {
-		// If we're called in a test there'll be a confusing error from the path
-		// functions below that gets reported without a stack trace, so let's panic
-		// properly with a more helpful message.
-		panic("This should not be called from tests. Please call GlobalSoongConfigForTests somewhere in the test setup.")
-	}
-
 	return &GlobalSoongConfig{
 		Profman:          ctx.Config().HostToolPath(ctx, "profman"),
 		Dex2oat:          dex2oatPathFromDep(ctx),
@@ -389,8 +382,7 @@
 // being at least one ordinary module with a Dex2oatDepTag dependency.
 //
 // TODO(b/147613152): Implement a way to deal with dependencies from singletons,
-// and then possibly remove this cache altogether (but the use in
-// GlobalSoongConfigForTests also needs to be rethought).
+// and then possibly remove this cache altogether.
 var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig")
 
 // GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called,
@@ -550,18 +542,14 @@
 	}
 }
 
-func GlobalSoongConfigForTests(config android.Config) *GlobalSoongConfig {
-	// Install the test GlobalSoongConfig in the Once cache so that later calls to
-	// Get(Cached)GlobalSoongConfig returns it without trying to create a real one.
-	return config.Once(globalSoongConfigOnceKey, func() interface{} {
-		return &GlobalSoongConfig{
-			Profman:          android.PathForTesting("profman"),
-			Dex2oat:          android.PathForTesting("dex2oat"),
-			Aapt:             android.PathForTesting("aapt"),
-			SoongZip:         android.PathForTesting("soong_zip"),
-			Zip2zip:          android.PathForTesting("zip2zip"),
-			ManifestCheck:    android.PathForTesting("manifest_check"),
-			ConstructContext: android.PathForTesting("construct_context"),
-		}
-	}).(*GlobalSoongConfig)
+func globalSoongConfigForTests() *GlobalSoongConfig {
+	return &GlobalSoongConfig{
+		Profman:          android.PathForTesting("profman"),
+		Dex2oat:          android.PathForTesting("dex2oat"),
+		Aapt:             android.PathForTesting("aapt"),
+		SoongZip:         android.PathForTesting("soong_zip"),
+		Zip2zip:          android.PathForTesting("zip2zip"),
+		ManifestCheck:    android.PathForTesting("manifest_check"),
+		ConstructContext: android.PathForTesting("construct_context"),
+	}
 }
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 59278fd..af73d0c 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -61,7 +61,7 @@
 func TestDexPreopt(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := GlobalSoongConfigForTests(config)
+	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testSystemModuleConfig(ctx, "test")
 
@@ -83,7 +83,7 @@
 func TestDexPreoptSystemOther(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := GlobalSoongConfigForTests(config)
+	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	systemModule := testSystemModuleConfig(ctx, "Stest")
 	systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
@@ -143,7 +143,7 @@
 func TestDexPreoptProfile(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := GlobalSoongConfigForTests(config)
+	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testSystemModuleConfig(ctx, "test")
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index c8f9538..004cbbb 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -25,6 +25,18 @@
 	"github.com/google/blueprint/proptools"
 )
 
+// =================================================================================================
+// WIP - see http://b/177892522 for details
+//
+// The build support for boot images is currently being migrated away from singleton to modules so
+// the documentation may not be strictly accurate. Rather than update the documentation at every
+// step which will create a lot of churn the changes that have been made will be listed here and the
+// documentation will be updated once it is closer to the final result.
+//
+// Changes:
+// 1) dex_bootjars is now a singleton module and not a plain singleton.
+// =================================================================================================
+
 // This comment describes:
 //   1. ART boot images in general (their types, structure, file layout, etc.)
 //   2. build system support for boot images
@@ -124,7 +136,7 @@
 // The primary ART boot image needs to be compiled with one dex2oat invocation that depends on DEX
 // jars for the core libraries. Framework boot image extension needs to be compiled with one dex2oat
 // invocation that depends on the primary ART boot image and all bootclasspath DEX jars except the
-// Core libraries.
+// core libraries as they are already part of the primary ART boot image.
 //
 // 2.1. Libraries that go in the boot images
 // -----------------------------------------
@@ -339,20 +351,24 @@
 	return append(imageLocations, dexpreopt.PathToLocation(image.images, image.target.Arch.ArchType))
 }
 
-func dexpreoptBootJarsFactory() android.Singleton {
-	return &dexpreoptBootJars{}
+func dexpreoptBootJarsFactory() android.SingletonModule {
+	m := &dexpreoptBootJars{}
+	android.InitAndroidModule(m)
+	return m
 }
 
 func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
+	ctx.RegisterSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
 }
 
 func SkipDexpreoptBootJars(ctx android.PathContext) bool {
 	return dexpreopt.GetGlobalConfig(ctx).DisablePreoptBootImages
 }
 
-// Singleton for generating boot image build rules.
+// Singleton module for generating boot image build rules.
 type dexpreoptBootJars struct {
+	android.SingletonModuleBase
+
 	// Default boot image config (currently always the Framework boot image extension). It should be
 	// noted that JIT-Zygote builds use ART APEX image instead of the Framework boot image extension,
 	// but the switch is handled not here, but in the makefiles (triggered with
@@ -385,8 +401,15 @@
 	return files
 }
 
+// Provide paths to boot images for use by modules that depend upon them.
+//
+// The build rules are created in GenerateSingletonBuildActions().
+func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Placeholder for now.
+}
+
 // Generate build rules for boot images.
-func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
+func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
 	if SkipDexpreoptBootJars(ctx) {
 		return
 	}
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 95fe5e1..48bc244 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -52,7 +52,6 @@
 	dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
 
 	ctx := testContext(config)
-	RegisterDexpreoptBootJarsComponents(ctx)
 	run(t, ctx, config)
 
 	dexpreoptBootJars := ctx.SingletonForTests("dex_bootjars")
diff --git a/java/java_test.go b/java/java_test.go
index 1c0738f..7b89848 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -61,33 +61,16 @@
 func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
 	bp += dexpreopt.BpToolModulesForTest()
 
-	config := TestConfig(buildDir, env, bp, fs)
-
-	// Set up the global Once cache used for dexpreopt.GlobalSoongConfig, so that
-	// it doesn't create a real one, which would fail.
-	_ = dexpreopt.GlobalSoongConfigForTests(config)
-
-	return config
+	return TestConfig(buildDir, env, bp, fs)
 }
 
 func testContext(config android.Config) *android.TestContext {
 
 	ctx := android.NewTestArchContext(config)
-	RegisterJavaBuildComponents(ctx)
-	RegisterAppBuildComponents(ctx)
-	RegisterAppImportBuildComponents(ctx)
-	RegisterAppSetBuildComponents(ctx)
-	RegisterAARBuildComponents(ctx)
-	RegisterGenRuleBuildComponents(ctx)
-	RegisterRuntimeResourceOverlayBuildComponents(ctx)
-	RegisterSystemModulesBuildComponents(ctx)
+	RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.RegisterModuleType("java_plugin", PluginFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("python_binary_host", python.PythonBinaryHostFactory)
-	RegisterDocsBuildComponents(ctx)
-	RegisterStubsBuildComponents(ctx)
-	RegisterPrebuiltApisBuildComponents(ctx)
-	RegisterSdkLibraryBuildComponents(ctx)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterComponentsMutator)
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 1a655a6..638740f 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1048,6 +1048,30 @@
 
 // Add other dependencies as normal.
 func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+	var missingApiModules []string
+	for _, apiScope := range module.getGeneratedApiScopes(ctx) {
+		if apiScope.unstable {
+			continue
+		}
+		if m := android.SrcIsModule(module.latestApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+			missingApiModules = append(missingApiModules, m)
+		}
+		if m := android.SrcIsModule(module.latestRemovedApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+			missingApiModules = append(missingApiModules, m)
+		}
+	}
+	if len(missingApiModules) != 0 && !module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api {
+		m := module.Name() + " is missing tracking files for previously released library versions.\n"
+		m += "You need to do one of the following:\n"
+		m += "- Add `unsafe_ignore_missing_latest_api: true` to your blueprint (to disable compat tracking)\n"
+		m += "- Add a set of prebuilt txt files representing the last released version of this library for compat checking.\n"
+		m += "  (the current set of API files can be used as a seed for this compatibility tracking\n"
+		m += "\n"
+		m += "The following filegroup modules are missing:\n  "
+		m += strings.Join(missingApiModules, "\n  ") + "\n"
+		m += "Please see the documentation of the prebuilt_apis module type (and a usage example in prebuilts/sdk) for a convenient way to generate these."
+		ctx.ModuleErrorf(m)
+	}
 	if module.requiresRuntimeImplementationLibrary() {
 		// Only add the deps for the library if it is actually going to be built.
 		module.Library.deps(ctx)
diff --git a/java/testing.go b/java/testing.go
index fc4e477..0b1e2eb 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -95,6 +95,29 @@
 	return fs
 }
 
+// Register build components provided by this package that are needed by tests.
+//
+// In particular this must register all the components that are used in the `Android.bp` snippet
+// returned by GatherRequiredDepsForTest()
+func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+	RegisterAARBuildComponents(ctx)
+	RegisterAppBuildComponents(ctx)
+	RegisterAppImportBuildComponents(ctx)
+	RegisterAppSetBuildComponents(ctx)
+	RegisterDexpreoptBootJarsComponents(ctx)
+	RegisterDocsBuildComponents(ctx)
+	RegisterGenRuleBuildComponents(ctx)
+	RegisterJavaBuildComponents(ctx)
+	RegisterPrebuiltApisBuildComponents(ctx)
+	RegisterRuntimeResourceOverlayBuildComponents(ctx)
+	RegisterSdkLibraryBuildComponents(ctx)
+	RegisterStubsBuildComponents(ctx)
+	RegisterSystemModulesBuildComponents(ctx)
+}
+
+// Gather the module definitions needed by tests that depend upon code from this package.
+//
+// Returns an `Android.bp` snippet that defines the modules that are needed by this package.
 func GatherRequiredDepsForTest() string {
 	var bp string
 
@@ -181,6 +204,13 @@
 		`, extra)
 	}
 
+	// Make sure that the dex_bootjars singleton module is instantiated for the tests.
+	bp += `
+		dex_bootjars {
+			name: "dex_bootjars",
+		}
+`
+
 	return bp
 }
 
diff --git a/rust/builder.go b/rust/builder.go
index e5f0ab5..baeab69 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -188,7 +188,7 @@
 	implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
 	implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
 	implicits = append(implicits, deps.StaticLibs...)
-	implicits = append(implicits, deps.SharedLibs...)
+	implicits = append(implicits, deps.SharedLibDeps...)
 	implicits = append(implicits, deps.srcProviderFiles...)
 
 	if deps.CrtBegin.Valid() {
diff --git a/rust/rust.go b/rust/rust.go
index 1fa97af..83add87 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -267,14 +267,15 @@
 }
 
 type PathDeps struct {
-	DyLibs      RustLibraries
-	RLibs       RustLibraries
-	SharedLibs  android.Paths
-	StaticLibs  android.Paths
-	ProcMacros  RustLibraries
-	linkDirs    []string
-	depFlags    []string
-	linkObjects []string
+	DyLibs        RustLibraries
+	RLibs         RustLibraries
+	SharedLibs    android.Paths
+	SharedLibDeps android.Paths
+	StaticLibs    android.Paths
+	ProcMacros    RustLibraries
+	linkDirs      []string
+	depFlags      []string
+	linkObjects   []string
 	//ReexportedDeps android.Paths
 
 	// Used by bindgen modules which call clang
@@ -952,9 +953,15 @@
 		staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile().Path())
 	}
 
+	var sharedLibFiles android.Paths
 	var sharedLibDepFiles android.Paths
 	for _, dep := range directSharedLibDeps {
-		sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+		sharedLibFiles = append(sharedLibFiles, dep.OutputFile().Path())
+		if dep.Toc().Valid() {
+			sharedLibDepFiles = append(sharedLibDepFiles, dep.Toc().Path())
+		} else {
+			sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+		}
 	}
 
 	var srcProviderDepFiles android.Paths
@@ -970,12 +977,14 @@
 	depPaths.RLibs = append(depPaths.RLibs, rlibDepFiles...)
 	depPaths.DyLibs = append(depPaths.DyLibs, dylibDepFiles...)
 	depPaths.SharedLibs = append(depPaths.SharedLibs, sharedLibDepFiles...)
+	depPaths.SharedLibDeps = append(depPaths.SharedLibDeps, sharedLibDepFiles...)
 	depPaths.StaticLibs = append(depPaths.StaticLibs, staticLibDepFiles...)
 	depPaths.ProcMacros = append(depPaths.ProcMacros, procMacroDepFiles...)
 	depPaths.SrcDeps = append(depPaths.SrcDeps, srcProviderDepFiles...)
 
 	// Dedup exported flags from dependencies
 	depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
+	depPaths.linkObjects = android.FirstUniqueStrings(depPaths.linkObjects)
 	depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
 	depPaths.depClangFlags = android.FirstUniqueStrings(depPaths.depClangFlags)
 	depPaths.depIncludePaths = android.FirstUniquePaths(depPaths.depIncludePaths)
diff --git a/sdk/testing.go b/sdk/testing.go
index 38755a9..1ac873b 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -107,12 +107,7 @@
 	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
 
 	// from java package
-	java.RegisterJavaBuildComponents(ctx)
-	java.RegisterAppBuildComponents(ctx)
-	java.RegisterSdkLibraryBuildComponents(ctx)
-	java.RegisterPrebuiltApisBuildComponents(ctx)
-	java.RegisterStubsBuildComponents(ctx)
-	java.RegisterSystemModulesBuildComponents(ctx)
+	java.RegisterRequiredBuildComponentsForTest(ctx)
 
 	// from cc package
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index b8b93f6..5cb9e64 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -58,9 +58,7 @@
 func testContext(config android.Config) *android.TestContext {
 
 	ctx := android.NewTestArchContext(config)
-	java.RegisterJavaBuildComponents(ctx)
-	java.RegisterAppBuildComponents(ctx)
-	java.RegisterSystemModulesBuildComponents(ctx)
+	java.RegisterRequiredBuildComponentsForTest(ctx)
 
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 06f6c63..a5986b2 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -117,6 +117,7 @@
 		productOut("misc_info.txt"),
 		productOut("apex"),
 		productOut("kernel"),
+		productOut("kernel-*"),
 		productOut("data"),
 		productOut("skin"),
 		productOut("obj/NOTICE_FILES"),