Merge "Manifest Fixer Params code refactor"
diff --git a/android/Android.bp b/android/Android.bp
index da36959..d3540b2 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -16,7 +16,9 @@
         "soong-remoteexec",
         "soong-response",
         "soong-shared",
+        "soong-starlark-format",
         "soong-ui-metrics_proto",
+
         "golang-protobuf-proto",
         "golang-protobuf-encoding-prototext",
 
diff --git a/android/bazel.go b/android/bazel.go
index 3046edb..7e5736f 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -288,6 +288,8 @@
 		"development/samples/WiFiDirectDemo":                 Bp2BuildDefaultTrue,
 		"development/sdk":                                    Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines":                    Bp2BuildDefaultTrueRecursively,
+		"external/auto/common":                               Bp2BuildDefaultTrueRecursively,
+		"external/auto/service":                              Bp2BuildDefaultTrueRecursively,
 		"external/boringssl":                                 Bp2BuildDefaultTrueRecursively,
 		"external/bouncycastle":                              Bp2BuildDefaultTrue,
 		"external/brotli":                                    Bp2BuildDefaultTrue,
@@ -300,6 +302,7 @@
 		"external/icu":                                       Bp2BuildDefaultTrueRecursively,
 		"external/icu/android_icu4j":                         Bp2BuildDefaultFalse, // java rules incomplete
 		"external/icu/icu4j":                                 Bp2BuildDefaultFalse, // java rules incomplete
+		"external/javapoet":                                  Bp2BuildDefaultTrueRecursively,
 		"external/jemalloc_new":                              Bp2BuildDefaultTrueRecursively,
 		"external/jsoncpp":                                   Bp2BuildDefaultTrueRecursively,
 		"external/libcap":                                    Bp2BuildDefaultTrueRecursively,
diff --git a/android/config.go b/android/config.go
index 10e074c..f10732b 100644
--- a/android/config.go
+++ b/android/config.go
@@ -38,6 +38,7 @@
 	"android/soong/android/soongconfig"
 	"android/soong/bazel"
 	"android/soong/remoteexec"
+	"android/soong/starlark_fmt"
 )
 
 // Bool re-exports proptools.Bool for the android package.
@@ -286,14 +287,12 @@
 		}
 	}
 
-	//TODO(b/216168792) should use common function to print Starlark code
-	nonArchVariantProductVariablesJson, err := json.MarshalIndent(&nonArchVariantProductVariables, "", "    ")
+	nonArchVariantProductVariablesJson := starlark_fmt.PrintStringList(nonArchVariantProductVariables, 0)
 	if err != nil {
 		return fmt.Errorf("cannot marshal product variable data: %s", err.Error())
 	}
 
-	//TODO(b/216168792) should use common function to print Starlark code
-	archVariantProductVariablesJson, err := json.MarshalIndent(&archVariantProductVariables, "", "    ")
+	archVariantProductVariablesJson := starlark_fmt.PrintStringList(archVariantProductVariables, 0)
 	if err != nil {
 		return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error())
 	}
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index acb9d18..ceb8e45 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -386,6 +386,46 @@
 	})).RunTest(t)
 }
 
+func TestDuplicateStringValueInSoongConfigStringVariable(t *testing.T) {
+	bp := `
+		soong_config_string_variable {
+			name: "board",
+			values: ["soc_a", "soc_b", "soc_c", "soc_a"],
+		}
+
+		soong_config_module_type {
+			name: "acme_test",
+			module_type: "test",
+			config_namespace: "acme",
+			variables: ["board"],
+			properties: ["cflags", "srcs", "defaults"],
+		}
+    `
+
+	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
+		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.VendorVars = vars
+		})
+	}
+
+	GroupFixturePreparers(
+		fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
+		PrepareForTestWithDefaults,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			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)
+		}),
+		FixtureWithRootAndroidBp(bp),
+	).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
+		// TODO(b/171232169): improve the error message for non-existent properties
+		`Android.bp: soong_config_string_variable: values property error: duplicate value: "soc_a"`,
+	})).RunTest(t)
+}
+
 func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
 	config := TestConfig(buildDir, nil, bp, fs)
 
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index 9bf3344..8fe1ff1 100644
--- a/android/soongconfig/Android.bp
+++ b/android/soongconfig/Android.bp
@@ -10,6 +10,7 @@
         "blueprint-parser",
         "blueprint-proptools",
         "soong-bazel",
+        "soong-starlark-format",
     ],
     srcs: [
         "config.go",
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 09a5057..212b752 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -25,6 +25,8 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/parser"
 	"github.com/google/blueprint/proptools"
+
+	"android/soong/starlark_fmt"
 )
 
 const conditionsDefault = "conditions_default"
@@ -177,10 +179,14 @@
 		return []error{fmt.Errorf("values property must be set")}
 	}
 
+	vals := make(map[string]bool, len(stringProps.Values))
 	for _, name := range stringProps.Values {
 		if err := checkVariableName(name); err != nil {
 			return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
+		} else if _, ok := vals[name]; ok {
+			return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
 		}
+		vals[name] = true
 	}
 
 	v.variables[base.variable] = &stringVariable{
@@ -235,7 +241,12 @@
 // 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
+	// varCache contains a cache of string variables namespace + property
+	// The same variable may be used in multiple module types (for example, if need support
+	// for cc_default and java_default), only need to process once
+	varCache map[string]bool
+
+	StringVars map[string][]string
 	BoolVars   map[string]bool
 	ValueVars  map[string]bool
 }
@@ -253,7 +264,7 @@
 	defer bp2buildSoongConfigVarsLock.Unlock()
 
 	if defs.StringVars == nil {
-		defs.StringVars = make(map[string]map[string]bool)
+		defs.StringVars = make(map[string][]string)
 	}
 	if defs.BoolVars == nil {
 		defs.BoolVars = make(map[string]bool)
@@ -261,15 +272,24 @@
 	if defs.ValueVars == nil {
 		defs.ValueVars = make(map[string]bool)
 	}
+	if defs.varCache == nil {
+		defs.varCache = make(map[string]bool)
+	}
 	for _, moduleType := range mtDef.ModuleTypes {
 		for _, v := range moduleType.Variables {
 			key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
+
+			// The same variable may be used in multiple module types (for example, if need support
+			// for cc_default and java_default), only need to process once
+			if _, keyInCache := defs.varCache[key]; keyInCache {
+				continue
+			} else {
+				defs.varCache[key] = true
+			}
+
 			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
+					defs.StringVars[key] = append(defs.StringVars[key], value)
 				}
 			} else if _, ok := v.(*boolVariable); ok {
 				defs.BoolVars[key] = true
@@ -302,29 +322,16 @@
 // 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_bool_variables = "
+	ret += starlark_fmt.PrintBoolDict(defs.BoolVars, 0)
+	ret += "\n\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_value_variables = "
+	ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
+	ret += "\n\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 += "}"
+	ret += "soong_config_string_variables = "
+	ret += starlark_fmt.PrintStringListDict(defs.StringVars, 0)
 
 	return ret
 }
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index b14f8b4..a7800e8 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -367,19 +367,19 @@
 
 func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
 	testCases := []struct {
+		desc     string
 		defs     Bp2BuildSoongConfigDefinitions
 		expected string
 	}{
 		{
+			desc: "all empty",
 			defs: Bp2BuildSoongConfigDefinitions{},
-			expected: `soong_config_bool_variables = {
-}
+			expected: `soong_config_bool_variables = {}
 
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
 
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+			desc: "only bool",
 			defs: Bp2BuildSoongConfigDefinitions{
 				BoolVars: map[string]bool{
 					"bool_var": true,
@@ -389,39 +389,35 @@
     "bool_var": True,
 }
 
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
 
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+			desc: "only value vars",
 			defs: Bp2BuildSoongConfigDefinitions{
 				ValueVars: map[string]bool{
 					"value_var": true,
 				},
 			},
-			expected: `soong_config_bool_variables = {
-}
+			expected: `soong_config_bool_variables = {}
 
 soong_config_value_variables = {
     "value_var": True,
 }
 
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+			desc: "only string vars",
 			defs: Bp2BuildSoongConfigDefinitions{
-				StringVars: map[string]map[string]bool{
-					"string_var": map[string]bool{
-						"choice1": true,
-						"choice2": true,
-						"choice3": true,
+				StringVars: map[string][]string{
+					"string_var": []string{
+						"choice1",
+						"choice2",
+						"choice3",
 					},
 				},
 			},
-			expected: `soong_config_bool_variables = {
-}
+			expected: `soong_config_bool_variables = {}
 
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
 
 soong_config_string_variables = {
     "string_var": [
@@ -430,6 +426,7 @@
         "choice3",
     ],
 }`}, {
+			desc: "all vars",
 			defs: Bp2BuildSoongConfigDefinitions{
 				BoolVars: map[string]bool{
 					"bool_var_one": true,
@@ -438,15 +435,15 @@
 					"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,
+				StringVars: map[string][]string{
+					"string_var_one": []string{
+						"choice1",
+						"choice2",
+						"choice3",
 					},
-					"string_var_two": map[string]bool{
-						"foo": true,
-						"bar": true,
+					"string_var_two": []string{
+						"foo",
+						"bar",
 					},
 				},
 			},
@@ -466,15 +463,17 @@
         "choice3",
     ],
     "string_var_two": [
-        "bar",
         "foo",
+        "bar",
     ],
 }`},
 	}
 	for _, test := range testCases {
-		actual := test.defs.String()
-		if actual != test.expected {
-			t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
-		}
+		t.Run(test.desc, func(t *testing.T) {
+			actual := test.defs.String()
+			if actual != test.expected {
+				t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
+			}
+		})
 	}
 }
diff --git a/apex/builder.go b/apex/builder.go
index 7c85231..fc4bf8a 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -608,6 +608,8 @@
 
 			implicitInputs = append(implicitInputs, androidManifestFile)
 			optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
+		} else if a.testApex {
+			optFlags = append(optFlags, "--test_only")
 		}
 
 		// Determine target/min sdk version from the context
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 4bcfa61..b904c35 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -28,6 +28,7 @@
         "soong-genrule",
         "soong-python",
         "soong-sh",
+        "soong-starlark-format",
         "soong-ui-metrics",
     ],
     testSrcs: [
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index b3bec65..1d3b105 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -27,6 +27,7 @@
 
 	"android/soong/android"
 	"android/soong/bazel"
+	"android/soong/starlark_fmt"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -559,48 +560,27 @@
 		return "", nil
 	}
 
-	var ret string
 	switch propertyValue.Kind() {
 	case reflect.String:
-		ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
+		return fmt.Sprintf("\"%v\"", escapeString(propertyValue.String())), nil
 	case reflect.Bool:
-		ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
+		return starlark_fmt.PrintBool(propertyValue.Bool()), nil
 	case reflect.Int, reflect.Uint, reflect.Int64:
-		ret = fmt.Sprintf("%v", propertyValue.Interface())
+		return fmt.Sprintf("%v", propertyValue.Interface()), nil
 	case reflect.Ptr:
 		return prettyPrint(propertyValue.Elem(), indent, emitZeroValues)
 	case reflect.Slice:
-		if propertyValue.Len() == 0 {
-			return "[]", nil
-		}
-
-		if propertyValue.Len() == 1 {
-			// Single-line list for list with only 1 element
-			ret += "["
-			indexedValue, err := prettyPrint(propertyValue.Index(0), indent, emitZeroValues)
+		elements := make([]string, 0, propertyValue.Len())
+		for i := 0; i < propertyValue.Len(); i++ {
+			val, err := prettyPrint(propertyValue.Index(i), indent, emitZeroValues)
 			if err != nil {
 				return "", err
 			}
-			ret += indexedValue
-			ret += "]"
-		} else {
-			// otherwise, use a multiline list.
-			ret += "[\n"
-			for i := 0; i < propertyValue.Len(); i++ {
-				indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1, emitZeroValues)
-				if err != nil {
-					return "", err
-				}
-
-				if indexedValue != "" {
-					ret += makeIndent(indent + 1)
-					ret += indexedValue
-					ret += ",\n"
-				}
+			if val != "" {
+				elements = append(elements, val)
 			}
-			ret += makeIndent(indent)
-			ret += "]"
 		}
+		return starlark_fmt.PrintList(elements, indent, "%s"), nil
 
 	case reflect.Struct:
 		// Special cases where the bp2build sends additional information to the codegenerator
@@ -611,18 +591,12 @@
 			return fmt.Sprintf("%q", label.Label), nil
 		}
 
-		ret = "{\n"
 		// Sort and print the struct props by the key.
 		structProps := extractStructProperties(propertyValue, indent)
 		if len(structProps) == 0 {
 			return "", nil
 		}
-		for _, k := range android.SortedStringKeys(structProps) {
-			ret += makeIndent(indent + 1)
-			ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
-		}
-		ret += makeIndent(indent)
-		ret += "}"
+		return starlark_fmt.PrintDict(structProps, indent), nil
 	case reflect.Interface:
 		// TODO(b/164227191): implement pretty print for interfaces.
 		// Interfaces are used for for arch, multilib and target properties.
@@ -631,7 +605,6 @@
 		return "", fmt.Errorf(
 			"unexpected kind for property struct field: %s", propertyValue.Kind())
 	}
-	return ret, nil
 }
 
 // Converts a reflected property struct value into a map of property names and property values,
@@ -736,13 +709,6 @@
 	return strings.ReplaceAll(s, "\"", "\\\"")
 }
 
-func makeIndent(indent int) string {
-	if indent < 0 {
-		panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
-	}
-	return strings.Repeat("    ", indent)
-}
-
 func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
 	name := ""
 	if c.ModuleSubDir(logicModule) != "" {
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index ee19783..3bf0221 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -1287,6 +1287,7 @@
 		"strip":                    true,
 		"stubs_symbol_file":        true,
 		"stubs_versions":           true,
+		"inject_bssl_hash":         true,
 	}
 	sharedAttrs := attrNameToString{}
 	staticAttrs := attrNameToString{}
@@ -1822,6 +1823,33 @@
 	)
 }
 
+func TestLibcryptoHashInjection(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                "cc_library - libcrypto hash injection",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		filesystem:                 map[string]string{},
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "libcrypto",
+    target: {
+        android: {
+            inject_bssl_hash: true,
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		expectedBazelTargets: makeCcLibraryTargets("libcrypto", attrNameToString{
+			"inject_bssl_hash": `select({
+        "//build/bazel/platforms/os:android": True,
+        "//conditions:default": None,
+    })`,
+		}),
+	},
+	)
+}
+
 func TestCcLibraryCppStdWithGnuExtensions_ConvertsToFeatureAttr(t *testing.T) {
 	type testCase struct {
 		cpp_std        string
@@ -2409,3 +2437,18 @@
 	},
 	)
 }
+
+func TestCcLibraryEscapeLdflags(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	ldflags: ["-Wl,--rpath,${ORIGIN}"],
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: makeCcLibraryTargets("foo", attrNameToString{
+			"linkopts": `["-Wl,--rpath,$${ORIGIN}"]`,
+		}),
+	})
+}
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index dfbb265..d37a523 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -6,6 +6,7 @@
 
 	"android/soong/android"
 	"android/soong/bazel"
+	"android/soong/starlark_fmt"
 )
 
 // Configurability support for bp2build.
@@ -250,10 +251,10 @@
 	} else if defaultValue != nil {
 		// Print an explicit empty list (the default value) even if the value is
 		// empty, to avoid errors about not finding a configuration that matches.
-		ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
+		ret += fmt.Sprintf("%s\"%s\": %s,\n", starlark_fmt.Indention(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
 	}
 
-	ret += makeIndent(indent)
+	ret += starlark_fmt.Indention(indent)
 	ret += "})"
 
 	return ret, nil
@@ -262,7 +263,7 @@
 // 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, emitZeroValues bool) (string, error) {
-	s := makeIndent(indent + 1)
+	s := starlark_fmt.Indention(indent + 1)
 	v, err := prettyPrint(value, indent+1, emitZeroValues)
 	if err != nil {
 		return "", err
diff --git a/bpf/bpf.go b/bpf/bpf.go
index a4999e5..14b2d84 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -20,7 +20,6 @@
 	"strings"
 
 	"android/soong/android"
-	_ "android/soong/cc/config"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -74,7 +73,10 @@
 	Include_dirs []string
 	Sub_dir      string
 	// If set to true, generate BTF debug info for maps & programs
-	Btf          *bool
+	Btf    *bool
+	Vendor *bool
+
+	VendorInternal bool `blueprint:"mutated"`
 }
 
 type bpf struct {
@@ -85,6 +87,41 @@
 	objs android.Paths
 }
 
+var _ android.ImageInterface = (*bpf)(nil)
+
+func (bpf *bpf) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (bpf *bpf) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return !proptools.Bool(bpf.properties.Vendor)
+}
+
+func (bpf *bpf) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (bpf *bpf) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (bpf *bpf) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (bpf *bpf) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (bpf *bpf) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	if proptools.Bool(bpf.properties.Vendor) {
+		return []string{"vendor"}
+	}
+	return nil
+}
+
+func (bpf *bpf) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+	bpf.properties.VendorInternal = variation == "vendor"
+}
+
 func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	cflags := []string{
 		"-nostdlibinc",
@@ -132,8 +169,8 @@
 		if proptools.Bool(bpf.properties.Btf) {
 			objStripped := android.ObjPathWithExt(ctx, "", src, "o")
 			ctx.Build(pctx, android.BuildParams{
-				Rule: stripRule,
-				Input: obj,
+				Rule:   stripRule,
+				Input:  obj,
 				Output: objStripped,
 				Args: map[string]string{
 					"stripCmd": "${config.ClangBin}/llvm-strip",
@@ -154,7 +191,12 @@
 			fmt.Fprintln(w)
 			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
 			fmt.Fprintln(w)
-			localModulePath := "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf"
+			var localModulePath string
+			if bpf.properties.VendorInternal {
+				localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/bpf"
+			} else {
+				localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf"
+			}
 			if len(bpf.properties.Sub_dir) > 0 {
 				localModulePath += "/" + bpf.properties.Sub_dir
 			}
diff --git a/cc/binary.go b/cc/binary.go
index 05923b1..54fd339 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -607,7 +607,7 @@
 
 	ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_binary",
-		Bzl_load_location: "//build/bazel/rules:cc_binary.bzl",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_binary.bzl",
 	},
 		android.CommonAttributes{Name: m.Name()},
 		attrs)
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 30c3c50..42fc0e4 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -644,7 +644,7 @@
 
 	var linkerFlags []string
 	if len(props.Ldflags) > 0 {
-		linkerFlags = append(linkerFlags, props.Ldflags...)
+		linkerFlags = append(linkerFlags, proptools.NinjaEscapeList(props.Ldflags)...)
 		// binaries remove static flag if -shared is in the linker flags
 		if isBinary && android.InList("-shared", linkerFlags) {
 			axisFeatures = append(axisFeatures, "-static_flag")
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 7b7ee28..e1b0605 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -8,6 +8,7 @@
     deps: [
         "soong-android",
         "soong-remoteexec",
+        "soong-starlark-format",
     ],
     srcs: [
         "bp2build.go",
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
index 982b436..eca5161 100644
--- a/cc/config/bp2build.go
+++ b/cc/config/bp2build.go
@@ -22,14 +22,11 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/starlark_fmt"
 
 	"github.com/google/blueprint"
 )
 
-const (
-	bazelIndent = 4
-)
-
 type bazelVarExporter interface {
 	asBazel(android.Config, exportedStringVariables, exportedStringListVariables, exportedConfigDependingVariables) []bazelConstant
 }
@@ -73,21 +70,6 @@
 	m[k] = v
 }
 
-func bazelIndention(level int) string {
-	return strings.Repeat(" ", level*bazelIndent)
-}
-
-func printBazelList(items []string, indentLevel int) string {
-	list := make([]string, 0, len(items)+2)
-	list = append(list, "[")
-	innerIndent := bazelIndention(indentLevel + 1)
-	for _, item := range items {
-		list = append(list, fmt.Sprintf(`%s"%s",`, innerIndent, item))
-	}
-	list = append(list, bazelIndention(indentLevel)+"]")
-	return strings.Join(list, "\n")
-}
-
 func (m exportedStringVariables) asBazel(config android.Config,
 	stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant {
 	ret := make([]bazelConstant, 0, len(m))
@@ -139,7 +121,7 @@
 		// out through a constants struct later.
 		ret = append(ret, bazelConstant{
 			variableName:       k,
-			internalDefinition: printBazelList(expandedVars, 0),
+			internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
 		})
 	}
 	return ret
@@ -173,17 +155,6 @@
 	m[k] = v
 }
 
-func printBazelStringListDict(dict map[string][]string) string {
-	bazelDict := make([]string, 0, len(dict)+2)
-	bazelDict = append(bazelDict, "{")
-	for k, v := range dict {
-		bazelDict = append(bazelDict,
-			fmt.Sprintf(`%s"%s": %s,`, bazelIndention(1), k, printBazelList(v, 1)))
-	}
-	bazelDict = append(bazelDict, "}")
-	return strings.Join(bazelDict, "\n")
-}
-
 // Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
 func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
 	_ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
@@ -191,7 +162,7 @@
 	for k, dict := range m {
 		ret = append(ret, bazelConstant{
 			variableName:       k,
-			internalDefinition: printBazelStringListDict(dict),
+			internalDefinition: starlark_fmt.PrintStringListDict(dict, 0),
 		})
 	}
 	return ret
@@ -223,7 +194,7 @@
 		definitions = append(definitions,
 			fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
 		constants = append(constants,
-			fmt.Sprintf("%[1]s%[2]s = _%[2]s,", bazelIndention(1), b.variableName))
+			fmt.Sprintf("%[1]s%[2]s = _%[2]s,", starlark_fmt.Indention(1), b.variableName))
 	}
 
 	// Build the exported constants struct.
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
index 3118df1..4cbf0c6 100644
--- a/cc/config/bp2build_test.go
+++ b/cc/config/bp2build_test.go
@@ -211,15 +211,11 @@
 			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
 
 _a = {
-    "b1": [
-        "b2",
-    ],
+    "b1": ["b2"],
 }
 
 _c = {
-    "d1": [
-        "d2",
-    ],
+    "d1": ["d2"],
 }
 
 constants = struct(
@@ -246,27 +242,19 @@
 			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
 
 _a = {
-    "a1": [
-        "a2",
-    ],
+    "a1": ["a2"],
 }
 
 _b = "b-val"
 
-_c = [
-    "c-val",
-]
+_c = ["c-val"]
 
 _d = "d-val"
 
-_e = [
-    "e-val",
-]
+_e = ["e-val"]
 
 _f = {
-    "f1": [
-        "f2",
-    ],
+    "f1": ["f2"],
 }
 
 constants = struct(
diff --git a/cc/library.go b/cc/library.go
index de5d46f..708aa10 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -32,7 +32,7 @@
 	"github.com/google/blueprint/pathtools"
 )
 
-// LibraryProperties is a collection of properties shared by cc library rules.
+// LibraryProperties is a collection of properties shared by cc library rules/cc.
 type LibraryProperties struct {
 	// local file name to pass to the linker as -unexported_symbols_list
 	Unexported_symbols_list *string `android:"path,arch_variant"`
@@ -386,13 +386,28 @@
 		Stubs_versions:    compilerAttrs.stubsVersions,
 	}
 
+	for axis, configToProps := range m.GetArchVariantProperties(ctx, &LibraryProperties{}) {
+		for config, props := range configToProps {
+			if props, ok := props.(*LibraryProperties); ok {
+				if props.Inject_bssl_hash != nil {
+					// This is an edge case applies only to libcrypto
+					if m.Name() == "libcrypto" {
+						sharedTargetAttrs.Inject_bssl_hash.SetSelectValue(axis, config, props.Inject_bssl_hash)
+					} else {
+						ctx.PropertyErrorf("inject_bssl_hash", "only applies to libcrypto")
+					}
+				}
+			}
+		}
+	}
+
 	staticProps := bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_library_static",
-		Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_library_static.bzl",
 	}
 	sharedProps := bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_library_shared",
-		Bzl_load_location: "//build/bazel/rules:cc_library_shared.bzl",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_library_shared.bzl",
 	}
 
 	ctx.CreateBazelTargetModuleWithRestrictions(staticProps,
@@ -2537,7 +2552,7 @@
 	}
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        modType,
-		Bzl_load_location: fmt.Sprintf("//build/bazel/rules:%s.bzl", modType),
+		Bzl_load_location: fmt.Sprintf("//build/bazel/rules/cc:%s.bzl", modType),
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
@@ -2602,4 +2617,5 @@
 
 	Stubs_symbol_file *string
 	Stubs_versions    bazel.StringListAttribute
+	Inject_bssl_hash  bazel.BoolAttribute
 }
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 064e2b8..5d38fba 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -136,7 +136,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_library_headers",
-		Bzl_load_location: "//build/bazel/rules:cc_library_headers.bzl",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_library_headers.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
diff --git a/cc/object.go b/cc/object.go
index 24f6ed4..fdd0b11 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -195,7 +195,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_object",
-		Bzl_load_location: "//build/bazel/rules:cc_object.bzl",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_object.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index c928ed9..339a16d 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -339,7 +339,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "prebuilt_library_static",
-		Bzl_load_location: "//build/bazel/rules:prebuilt_library_static.bzl",
+		Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_static.bzl",
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
@@ -359,7 +359,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "prebuilt_library_shared",
-		Bzl_load_location: "//build/bazel/rules:prebuilt_library_shared.bzl",
+		Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_shared.bzl",
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
diff --git a/cc/proto.go b/cc/proto.go
index f3410bc..3cf1453 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -210,7 +210,7 @@
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class:        rule_class,
-			Bzl_load_location: "//build/bazel/rules:cc_proto.bzl",
+			Bzl_load_location: "//build/bazel/rules/cc:cc_proto.bzl",
 		},
 		android.CommonAttributes{Name: name},
 		&protoAttrs)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 6c68822..b8e1468 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -705,19 +705,22 @@
 
 	if len(sanitize.Properties.Sanitizers) > 0 {
 		sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",")
-
 		flags.Local.CFlags = append(flags.Local.CFlags, sanitizeArg)
 		flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg)
-		if ctx.Host() {
+		flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
+
+		if ctx.toolchain().Bionic() {
+			// Bionic sanitizer runtimes have already been added as dependencies so that
+			// the right variant of the runtime will be used (with the "-android"
+			// suffix), so don't let clang the runtime library.
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-sanitize-link-runtime")
+		} else {
 			// Host sanitizers only link symbols in the final executable, so
 			// there will always be undefined symbols in intermediate libraries.
 			_, flags.Global.LdFlags = removeFromList("-Wl,--no-undefined", flags.Global.LdFlags)
-			flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
 
-			// non-Bionic toolchain prebuilts are missing UBSan's vptr and function sanitizers
-			if !ctx.toolchain().Bionic() {
-				flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
-			}
+			// non-Bionic toolchain prebuilts are missing UBSan's vptr and function san
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
 		}
 
 		if enableMinimalRuntime(sanitize) {
diff --git a/cc/tidy.go b/cc/tidy.go
index 1f5f56d..750e9de 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -102,6 +102,12 @@
 		}
 		flags.TidyFlags = append(flags.TidyFlags, headerFilter)
 	}
+	// Work around RBE bug in parsing clang-tidy flags, replace "--flag" with "-flag".
+	// Some C/C++ modules added local tidy flags like --header-filter= and --extra-arg-before=.
+	doubleDash := regexp.MustCompile("^('?)--(.*)$")
+	for i, s := range flags.TidyFlags {
+		flags.TidyFlags[i] = doubleDash.ReplaceAllString(s, "$1-$2")
+	}
 
 	// If clang-tidy is not enabled globally, add the -quiet flag.
 	if !ctx.Config().ClangTidy() {
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index d80051c..a0cfbea 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -205,14 +205,7 @@
 	buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
 		config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
 
-	{
-		var limits syscall.Rlimit
-		err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
-		if err != nil {
-			buildCtx.Verbosef("Failed to get file limit:", err)
-		}
-		buildCtx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
-	}
+	setMaxFiles(buildCtx)
 
 	{
 		// The order of the function calls is important. The last defer function call
@@ -614,3 +607,24 @@
 		}
 	}
 }
+
+func setMaxFiles(ctx build.Context) {
+	var limits syscall.Rlimit
+
+	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
+	if err != nil {
+		ctx.Println("Failed to get file limit:", err)
+		return
+	}
+
+	ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
+	if limits.Cur == limits.Max {
+		return
+	}
+
+	limits.Cur = limits.Max
+	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
+	if err != nil {
+		ctx.Println("Failed to increase file limit:", err)
+	}
+}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index a36bd6a..5fe409e 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -931,13 +931,13 @@
 	All_flags_path android.OptionalPath `supported_build_releases:"S"`
 
 	// The path to the generated signature-patterns.csv file.
-	Signature_patterns_path android.OptionalPath `supported_build_releases:"T+"`
+	Signature_patterns_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
 
 	// The path to the generated filtered-stub-flags.csv file.
-	Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"T+"`
+	Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
 
 	// The path to the generated filtered-flags.csv file.
-	Filtered_flags_path android.OptionalPath `supported_build_releases:"T+"`
+	Filtered_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
 }
 
 func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 6a2a7a8..7362cfb 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -2808,7 +2808,7 @@
 	StubsSrcJar    android.Path
 	CurrentApiFile android.Path
 	RemovedApiFile android.Path
-	AnnotationsZip android.Path `supported_build_releases:"T+"`
+	AnnotationsZip android.Path `supported_build_releases:"Tiramisu+"`
 	SdkVersion     string
 }
 
diff --git a/mk2rbc/cmd/mk2rbc.go b/mk2rbc/cmd/mk2rbc.go
index 8e23a53..e84eacd 100644
--- a/mk2rbc/cmd/mk2rbc.go
+++ b/mk2rbc/cmd/mk2rbc.go
@@ -40,7 +40,6 @@
 )
 
 var (
-	rootDir = flag.String("root", ".", "the value of // for load paths")
 	// TODO(asmundak): remove this option once there is a consensus on suffix
 	suffix   = flag.String("suffix", ".rbc", "generated files' suffix")
 	dryRun   = flag.Bool("dry_run", false, "dry run")
@@ -71,7 +70,6 @@
 		quit("cannot alias unknown flag " + target)
 	}
 	flagAlias("suffix", "s")
-	flagAlias("root", "d")
 	flagAlias("dry_run", "n")
 	flagAlias("convert_dependents", "r")
 	flagAlias("error_stat", "e")
@@ -91,6 +89,10 @@
 	}
 	flag.Parse()
 
+	if _, err := os.Stat("build/soong/mk2rbc"); err != nil {
+		quit("Must be run from the root of the android tree. (build/soong/mk2rbc does not exist)")
+	}
+
 	// Delouse
 	if *suffix == ".mk" {
 		quit("cannot use .mk as generated file suffix")
@@ -228,17 +230,16 @@
 	const androidProductsMk = "AndroidProducts.mk"
 	// Build the list of AndroidProducts.mk files: it's
 	// build/make/target/product/AndroidProducts.mk + device/**/AndroidProducts.mk plus + vendor/**/AndroidProducts.mk
-	targetAndroidProductsFile := filepath.Join(*rootDir, "build", "make", "target", "product", androidProductsMk)
+	targetAndroidProductsFile := filepath.Join("build", "make", "target", "product", androidProductsMk)
 	if _, err := os.Stat(targetAndroidProductsFile); err != nil {
-		fmt.Fprintf(os.Stderr, "%s: %s\n(hint: %s is not a source tree root)\n",
-			targetAndroidProductsFile, err, *rootDir)
+		fmt.Fprintf(os.Stderr, "%s: %s\n", targetAndroidProductsFile, err)
 	}
 	productConfigMap := make(map[string]string)
 	if err := mk2rbc.UpdateProductConfigMap(productConfigMap, targetAndroidProductsFile); err != nil {
 		fmt.Fprintf(os.Stderr, "%s: %s\n", targetAndroidProductsFile, err)
 	}
 	for _, t := range []string{"device", "vendor"} {
-		_ = filepath.WalkDir(filepath.Join(*rootDir, t),
+		_ = filepath.WalkDir(t,
 			func(path string, d os.DirEntry, err error) error {
 				if err != nil || d.IsDir() || filepath.Base(path) != androidProductsMk {
 					return nil
@@ -254,10 +255,9 @@
 }
 
 func getConfigVariables() {
-	path := filepath.Join(*rootDir, "build", "make", "core", "product.mk")
+	path := filepath.Join("build", "make", "core", "product.mk")
 	if err := mk2rbc.FindConfigVariables(path, mk2rbc.KnownVariables); err != nil {
-		quit(fmt.Errorf("%s\n(check --root[=%s], it should point to the source root)",
-			err, *rootDir))
+		quit(err)
 	}
 }
 
@@ -270,11 +270,11 @@
 	if name != "BUILD_SYSTEM" {
 		return fmt.Sprintf("$(%s)", name)
 	}
-	return filepath.Join(*rootDir, "build", "make", "core")
+	return filepath.Join("build", "make", "core")
 }
 
 func getSoongVariables() {
-	path := filepath.Join(*rootDir, "build", "make", "core", "soong_config.mk")
+	path := filepath.Join("build", "make", "core", "soong_config.mk")
 	err := mk2rbc.FindSoongVariables(path, fileNameScope{}, mk2rbc.KnownVariables)
 	if err != nil {
 		quit(err)
@@ -325,12 +325,11 @@
 	mk2starRequest := mk2rbc.Request{
 		MkFile:          mkFile,
 		Reader:          nil,
-		RootDir:         *rootDir,
 		OutputDir:       *outputTop,
 		OutputSuffix:    *suffix,
 		TracedVariables: tracedVariables,
 		TraceCalls:      *traceCalls,
-		SourceFS:        os.DirFS(*rootDir),
+		SourceFS:        os.DirFS("."),
 		MakefileFinder:  makefileFinder,
 		ErrorLogger:     errorLogger,
 	}
@@ -587,7 +586,7 @@
 
 func (l *FileListMakefileFinder) Find(root string) []string {
 	root, err1 := filepath.Abs(root)
-	wd, err2 := filepath.Abs(*rootDir)
+	wd, err2 := os.Getwd()
 	if root != wd || err1 != nil || err2 != nil {
 		return l.FindCommandMakefileFinder.Find(root)
 	}
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 03cf21e..b8fe162 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -142,7 +142,6 @@
 type Request struct {
 	MkFile          string    // file to convert
 	Reader          io.Reader // if set, read input from this stream instead
-	RootDir         string    // root directory path used to resolve included files
 	OutputSuffix    string    // generated Starlark files suffix
 	OutputDir       string    // if set, root of the output hierarchy
 	ErrorLogger     ErrorLogger
@@ -378,7 +377,6 @@
 	nodes          []starlarkNode
 	inherited      []*moduleInfo
 	hasErrors      bool
-	topDir         string
 	traceCalls     bool // print enter/exit each init function
 	sourceFS       fs.FS
 	makefileFinder MakefileFinder
@@ -414,11 +412,10 @@
 }
 
 func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
-	topdir, _ := filepath.Split(filepath.Join(ss.topDir, "foo"))
 	predefined := []struct{ name, value string }{
 		{"SRC_TARGET_DIR", filepath.Join("build", "make", "target")},
 		{"LOCAL_PATH", filepath.Dir(ss.mkFile)},
-		{"TOPDIR", topdir},
+		{"TOPDIR", ""}, // TOPDIR is just set to an empty string in cleanbuild.mk and core.mk
 		// TODO(asmundak): maybe read it from build/make/core/envsetup.mk?
 		{"TARGET_COPY_OUT_SYSTEM", "system"},
 		{"TARGET_COPY_OUT_SYSTEM_OTHER", "system_other"},
@@ -827,7 +824,7 @@
 }
 
 func (ctx *parseContext) findMatchingPaths(pattern []string) []string {
-	files := ctx.script.makefileFinder.Find(ctx.script.topDir)
+	files := ctx.script.makefileFinder.Find(".")
 	if len(pattern) == 0 {
 		return files
 	}
@@ -1864,7 +1861,6 @@
 	starScript := &StarlarkScript{
 		moduleName:     moduleNameForFile(req.MkFile),
 		mkFile:         req.MkFile,
-		topDir:         req.RootDir,
 		traceCalls:     req.TraceCalls,
 		sourceFS:       req.SourceFS,
 		makefileFinder: req.MakefileFinder,
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index c499398..a56815f 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -1119,7 +1119,7 @@
   rblf.inherit(handle, "foo/font", _font_init)
   # There's some space and even this comment between the include_top and the inherit-product
   rblf.inherit(handle, "foo/font", _font_init)
-  rblf.mkwarning("product.mk:11", "Including a path with a non-constant prefix, please convert this to a simple literal to generate cleaner starlark.")
+  rblf.mkwarning("product.mk:11", "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
   _entry = {
     "foo/font.mk": ("foo/font", _font_init),
     "bar/font.mk": ("bar/font", _font1_init),
@@ -1387,7 +1387,6 @@
 				ss, err := Convert(Request{
 					MkFile:         test.mkname,
 					Reader:         bytes.NewBufferString(test.in),
-					RootDir:        ".",
 					OutputSuffix:   ".star",
 					SourceFS:       fs,
 					MakefileFinder: &testMakefileFinder{fs: fs},
diff --git a/mk2rbc/node.go b/mk2rbc/node.go
index 61aaf91..5d98d7b 100644
--- a/mk2rbc/node.go
+++ b/mk2rbc/node.go
@@ -101,7 +101,7 @@
 func (i inheritedDynamicModule) emitSelect(gctx *generationContext) {
 	if i.needsWarning {
 		gctx.newLine()
-		gctx.writef("%s.mkwarning(%q, %q)", baseName, i.location, "Including a path with a non-constant prefix, please convert this to a simple literal to generate cleaner starlark.")
+		gctx.writef("%s.mkwarning(%q, %q)", baseName, i.location, "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
 	}
 	gctx.newLine()
 	gctx.writef("_entry = {")
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 0962168..14fcb02 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -37,6 +37,7 @@
 		"system/tools/aidl",
 		"tools/security/fuzzing/example_rust_fuzzer",
 		"tools/security/fuzzing/orphans",
+		"tools/security/remote_provisioning/cert_validator",
 		"tools/vendor",
 		"vendor/",
 	}
diff --git a/scripts/rustfmt.toml b/scripts/rustfmt.toml
index 617d425..cefaa42 100644
--- a/scripts/rustfmt.toml
+++ b/scripts/rustfmt.toml
@@ -1,5 +1,5 @@
 # Android Format Style
 
-edition = "2018"
+edition = "2021"
 use_small_heuristics = "Max"
 newline_style = "Unix"
diff --git a/sdk/build_release.go b/sdk/build_release.go
index 2bcdc6f..4c2277e 100644
--- a/sdk/build_release.go
+++ b/sdk/build_release.go
@@ -85,7 +85,7 @@
 
 	// Add the build releases from oldest to newest.
 	buildReleaseS = initBuildRelease("S")
-	buildReleaseT = initBuildRelease("T")
+	buildReleaseT = initBuildRelease("Tiramisu")
 )
 
 // initBuildRelease creates a new build release with the specified name.
diff --git a/sdk/build_release_test.go b/sdk/build_release_test.go
index 6608be4..6f1ef9e 100644
--- a/sdk/build_release_test.go
+++ b/sdk/build_release_test.go
@@ -60,7 +60,7 @@
 	t.Run("closed range", func(t *testing.T) {
 		set, err := parseBuildReleaseSet("S-F1")
 		android.AssertDeepEquals(t, "errors", nil, err)
-		android.AssertStringEquals(t, "set", "[S,T,F1]", set.String())
+		android.AssertStringEquals(t, "set", "[S,Tiramisu,F1]", set.String())
 	})
 	invalidAReleaseMessage := `unknown release "A", expected one of ` + allBuildReleaseSet.String()
 	t.Run("invalid release", func(t *testing.T) {
@@ -79,7 +79,7 @@
 		android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage)
 	})
 	t.Run("invalid release in closed range end", func(t *testing.T) {
-		set, err := parseBuildReleaseSet("T-A")
+		set, err := parseBuildReleaseSet("Tiramisu-A")
 		android.AssertDeepEquals(t, "set", (*buildReleaseSet)(nil), set)
 		android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage)
 	})
@@ -128,13 +128,13 @@
 
 	type mapped struct {
 		Default string
-		T_only  string `supported_build_releases:"T"`
+		T_only  string `supported_build_releases:"Tiramisu"`
 	}
 
 	type testBuildReleasePruner struct {
 		Default      string
-		S_and_T_only string `supported_build_releases:"S-T"`
-		T_later      string `supported_build_releases:"T+"`
+		S_and_T_only string `supported_build_releases:"S-Tiramisu"`
+		T_later      string `supported_build_releases:"Tiramisu+"`
 		Nested       nested
 		Mapped       map[string]*mapped
 	}
diff --git a/starlark_fmt/Android.bp b/starlark_fmt/Android.bp
new file mode 100644
index 0000000..8d80ccd
--- /dev/null
+++ b/starlark_fmt/Android.bp
@@ -0,0 +1,28 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-starlark-format",
+    pkgPath: "android/soong/starlark_fmt",
+    srcs: [
+        "format.go",
+    ],
+    testSrcs: [
+        "format_test.go",
+    ],
+}
diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go
new file mode 100644
index 0000000..23eee59
--- /dev/null
+++ b/starlark_fmt/format.go
@@ -0,0 +1,96 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package starlark_fmt
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+)
+
+const (
+	indent = 4
+)
+
+// Indention returns an indent string of the specified level.
+func Indention(level int) string {
+	if level < 0 {
+		panic(fmt.Errorf("indent level cannot be less than 0, but got %d", level))
+	}
+	return strings.Repeat(" ", level*indent)
+}
+
+// PrintBool returns a Starlark compatible bool string.
+func PrintBool(item bool) string {
+	return strings.Title(fmt.Sprintf("%t", item))
+}
+
+// PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels.
+func PrintStringList(items []string, indentLevel int) string {
+	return PrintList(items, indentLevel, `"%s"`)
+}
+
+// PrintList returns a Starlark-compatible string of list formmated as requested.
+func PrintList(items []string, indentLevel int, formatString string) string {
+	if len(items) == 0 {
+		return "[]"
+	} else if len(items) == 1 {
+		return fmt.Sprintf("["+formatString+"]", items[0])
+	}
+	list := make([]string, 0, len(items)+2)
+	list = append(list, "[")
+	innerIndent := Indention(indentLevel + 1)
+	for _, item := range items {
+		list = append(list, fmt.Sprintf(`%s`+formatString+`,`, innerIndent, item))
+	}
+	list = append(list, Indention(indentLevel)+"]")
+	return strings.Join(list, "\n")
+}
+
+// PrintStringListDict returns a Starlark-compatible string formatted as dictionary with
+// string keys and list of string values.
+func PrintStringListDict(dict map[string][]string, indentLevel int) string {
+	formattedValueDict := make(map[string]string, len(dict))
+	for k, v := range dict {
+		formattedValueDict[k] = PrintStringList(v, indentLevel+1)
+	}
+	return PrintDict(formattedValueDict, indentLevel)
+}
+
+// PrintBoolDict returns a starlark-compatible string containing a dictionary with string keys and
+// values printed with no additional formatting.
+func PrintBoolDict(dict map[string]bool, indentLevel int) string {
+	formattedValueDict := make(map[string]string, len(dict))
+	for k, v := range dict {
+		formattedValueDict[k] = PrintBool(v)
+	}
+	return PrintDict(formattedValueDict, indentLevel)
+}
+
+// PrintDict returns a starlark-compatible string containing a dictionary with string keys and
+// values printed with no additional formatting.
+func PrintDict(dict map[string]string, indentLevel int) string {
+	if len(dict) == 0 {
+		return "{}"
+	}
+	items := make([]string, 0, len(dict))
+	for k, v := range dict {
+		items = append(items, fmt.Sprintf(`%s"%s": %s,`, Indention(indentLevel+1), k, v))
+	}
+	sort.Strings(items)
+	return fmt.Sprintf(`{
+%s
+%s}`, strings.Join(items, "\n"), Indention(indentLevel))
+}
diff --git a/starlark_fmt/format_test.go b/starlark_fmt/format_test.go
new file mode 100644
index 0000000..90f78ef
--- /dev/null
+++ b/starlark_fmt/format_test.go
@@ -0,0 +1,169 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package starlark_fmt
+
+import (
+	"testing"
+)
+
+func TestPrintEmptyStringList(t *testing.T) {
+	in := []string{}
+	indentLevel := 0
+	out := PrintStringList(in, indentLevel)
+	expectedOut := "[]"
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintSingleElementStringList(t *testing.T) {
+	in := []string{"a"}
+	indentLevel := 0
+	out := PrintStringList(in, indentLevel)
+	expectedOut := `["a"]`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintMultiElementStringList(t *testing.T) {
+	in := []string{"a", "b"}
+	indentLevel := 0
+	out := PrintStringList(in, indentLevel)
+	expectedOut := `[
+    "a",
+    "b",
+]`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintEmptyList(t *testing.T) {
+	in := []string{}
+	indentLevel := 0
+	out := PrintList(in, indentLevel, "%s")
+	expectedOut := "[]"
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintSingleElementList(t *testing.T) {
+	in := []string{"1"}
+	indentLevel := 0
+	out := PrintList(in, indentLevel, "%s")
+	expectedOut := `[1]`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintMultiElementList(t *testing.T) {
+	in := []string{"1", "2"}
+	indentLevel := 0
+	out := PrintList(in, indentLevel, "%s")
+	expectedOut := `[
+    1,
+    2,
+]`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestListWithNonZeroIndent(t *testing.T) {
+	in := []string{"1", "2"}
+	indentLevel := 1
+	out := PrintList(in, indentLevel, "%s")
+	expectedOut := `[
+        1,
+        2,
+    ]`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestStringListDictEmpty(t *testing.T) {
+	in := map[string][]string{}
+	indentLevel := 0
+	out := PrintStringListDict(in, indentLevel)
+	expectedOut := `{}`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestStringListDict(t *testing.T) {
+	in := map[string][]string{
+		"key1": []string{},
+		"key2": []string{"a"},
+		"key3": []string{"1", "2"},
+	}
+	indentLevel := 0
+	out := PrintStringListDict(in, indentLevel)
+	expectedOut := `{
+    "key1": [],
+    "key2": ["a"],
+    "key3": [
+        "1",
+        "2",
+    ],
+}`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintDict(t *testing.T) {
+	in := map[string]string{
+		"key1": `""`,
+		"key2": `"a"`,
+		"key3": `[
+        1,
+        2,
+    ]`,
+	}
+	indentLevel := 0
+	out := PrintDict(in, indentLevel)
+	expectedOut := `{
+    "key1": "",
+    "key2": "a",
+    "key3": [
+        1,
+        2,
+    ],
+}`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintDictWithIndent(t *testing.T) {
+	in := map[string]string{
+		"key1": `""`,
+		"key2": `"a"`,
+	}
+	indentLevel := 1
+	out := PrintDict(in, indentLevel)
+	expectedOut := `{
+        "key1": "",
+        "key2": "a",
+    }`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}