Merge "Add host_linux target properties"
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..caa2a95
--- /dev/null
+++ b/METADATA
@@ -0,0 +1 @@
+name: "Android"
diff --git a/android/api_levels.go b/android/api_levels.go
index 926d297..de56625 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -321,6 +321,7 @@
 			"Q":     29,
 			"R":     30,
 			"S":     31,
+			"S-V2":  32,
 		}
 
 		// TODO: Differentiate "current" and "future".
@@ -364,6 +365,7 @@
 			"Q":     29,
 			"R":     30,
 			"S":     31,
+			"S-V2":  32,
 		}
 		for i, codename := range config.PlatformVersionActiveCodenames() {
 			apiLevelsMap[codename] = previewAPILevelBase + i
diff --git a/android/bazel.go b/android/bazel.go
index 7714f2c..342b840 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -208,6 +208,7 @@
 		"build/bazel/tests":/* recursive = */ true,
 		"build/bazel/platforms":/* recursive = */ true,
 		"build/bazel/product_variables":/* recursive = */ true,
+		"build/bazel/vendor/google":/* recursive = */ true,
 		"build/bazel_common_rules":/* recursive = */ true,
 		// build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
 		"build/make/tools":/* recursive = */ false,
@@ -225,8 +226,10 @@
 		"packages/apps/QuickSearchBox":/* recursive = */ true,
 		"packages/apps/WallpaperPicker":/* recursive = */ false,
 
+		"prebuilts/bundletool":/* recursive = */ true,
 		"prebuilts/gcc":/* recursive = */ true,
 		"prebuilts/build-tools":/* recursive = */ false,
+		"prebuilts/jdk/jdk11":/* recursive = */ false,
 		"prebuilts/sdk":/* recursive = */ false,
 		"prebuilts/sdk/current/extras/app-toolkit":/* recursive = */ false,
 		"prebuilts/sdk/current/support":/* recursive = */ false,
@@ -245,6 +248,7 @@
 		"build/bazel/examples/soong_config_variables":        Bp2BuildDefaultTrueRecursively,
 		"build/bazel/examples/apex/minimal":                  Bp2BuildDefaultTrueRecursively,
 		"build/make/tools/signapk":                           Bp2BuildDefaultTrue,
+		"build/make/target/product/security":                 Bp2BuildDefaultTrue,
 		"build/soong":                                        Bp2BuildDefaultTrue,
 		"build/soong/cc/libbuildversion":                     Bp2BuildDefaultTrue, // Skip tests subdir
 		"build/soong/cc/ndkstubgen":                          Bp2BuildDefaultTrue,
@@ -289,12 +293,14 @@
 		"development/samples/WiFiDirectDemo":                 Bp2BuildDefaultTrue,
 		"development/sdk":                                    Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines":                    Bp2BuildDefaultTrueRecursively,
+		"external/auto/android-annotation-stubs":             Bp2BuildDefaultTrueRecursively,
 		"external/auto/common":                               Bp2BuildDefaultTrueRecursively,
 		"external/auto/service":                              Bp2BuildDefaultTrueRecursively,
 		"external/boringssl":                                 Bp2BuildDefaultTrueRecursively,
 		"external/bouncycastle":                              Bp2BuildDefaultTrue,
 		"external/brotli":                                    Bp2BuildDefaultTrue,
 		"external/conscrypt":                                 Bp2BuildDefaultTrue,
+		"external/e2fsprogs/lib":                             Bp2BuildDefaultTrueRecursively,
 		"external/error_prone":                               Bp2BuildDefaultTrueRecursively,
 		"external/fmtlib":                                    Bp2BuildDefaultTrueRecursively,
 		"external/google-benchmark":                          Bp2BuildDefaultTrueRecursively,
@@ -325,6 +331,7 @@
 		"external/zstd":                                      Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
 		"frameworks/base/startop/apps/test":                  Bp2BuildDefaultTrue,
+		"frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/opengl/tests/gl2_cameraeye":       Bp2BuildDefaultTrue,
 		"frameworks/native/opengl/tests/gl2_java":            Bp2BuildDefaultTrue,
@@ -380,8 +387,36 @@
 	}
 
 	// Per-module allowlist to always opt modules in of both bp2build and mixed builds.
+	// These modules are usually in directories with many other modules that are not ready for
+	// conversion.
+	//
+	// A module can either be in this list or its directory allowlisted entirely
+	// in bp2buildDefaultConfig, but not both at the same time.
 	bp2buildModuleAlwaysConvertList = []string{
 		"junit-params-assertj-core",
+
+		//external/avb
+		"avbtool",
+		"libavb",
+		"avb_headers",
+
+		//external/fec
+		"libfec_rs",
+
+		//system/core/libsparse
+		"libsparse",
+
+		//system/extras/ext4_utils
+		"libext4_utils",
+
+		//system/extras/libfec
+		"libfec",
+
+		//system/extras/squashfs_utils
+		"libsquashfs_utils",
+
+		//system/extras/verity/fec
+		"fec",
 	}
 
 	// Per-module denylist to always opt modules out of both bp2build and mixed builds.
@@ -676,14 +711,21 @@
 	}
 
 	packagePath := ctx.OtherModuleDir(module)
-	config := ctx.Config().bp2buildPackageConfig
+	if alwaysConvert && ShouldKeepExistingBuildFileForDir(packagePath) {
+		ctx.(BaseModuleContext).ModuleErrorf("A module cannot be in a directory listed in bp2buildKeepExistingBuildFile"+
+			" and also be in bp2buildModuleAlwaysConvert. Directory: '%s'", packagePath)
 
+		return false
+	}
+
+	config := ctx.Config().bp2buildPackageConfig
 	// This is a tristate value: true, false, or unset.
 	propValue := b.bazelProperties.Bazel_module.Bp2build_available
 	if bp2buildDefaultTrueRecursively(packagePath, config) {
 		if alwaysConvert {
-			ctx.(BaseModuleContext).ModuleErrorf("a module cannot be in a directory marked Bp2BuildDefaultTrue" +
-				" or Bp2BuildDefaultTrueRecursively and also be in bp2buildModuleAlwaysConvert")
+			ctx.(BaseModuleContext).ModuleErrorf("A module cannot be in a directory marked Bp2BuildDefaultTrue"+
+				" or Bp2BuildDefaultTrueRecursively and also be in bp2buildModuleAlwaysConvert. Directory: '%s'",
+				packagePath)
 		}
 
 		// Allow modules to explicitly opt-out.
diff --git a/android/config.go b/android/config.go
index 3d8bc31..e8ca84c 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1256,6 +1256,10 @@
 	return Bool(c.config.productVariables.ClangCoverage)
 }
 
+func (c *deviceConfig) ClangCoverageContinuousMode() bool {
+	return Bool(c.config.productVariables.ClangCoverageContinuousMode)
+}
+
 func (c *deviceConfig) GcovCoverageEnabled() bool {
 	return Bool(c.config.productVariables.GcovCoverage)
 }
diff --git a/android/licenses.go b/android/licenses.go
index b51a06b..e60c7a2 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -333,4 +333,6 @@
 	ctx.Strict("HTMLNOTICE", ctx.Config().HostToolPath(ctx, "htmlnotice").String())
 	ctx.Strict("XMLNOTICE", ctx.Config().HostToolPath(ctx, "xmlnotice").String())
 	ctx.Strict("TEXTNOTICE", ctx.Config().HostToolPath(ctx, "textnotice").String())
+	ctx.Strict("COMPLIANCENOTICE_BOM", ctx.Config().HostToolPath(ctx, "compliancenotice_bom").String())
+	ctx.Strict("COMPLIANCENOTICE_SHIPPEDLIBS", ctx.Config().HostToolPath(ctx, "compliancenotice_shippedlibs").String())
 }
diff --git a/android/variable.go b/android/variable.go
index 68f19b9..37037eb 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -306,10 +306,11 @@
 	JavaCoveragePaths        []string `json:",omitempty"`
 	JavaCoverageExcludePaths []string `json:",omitempty"`
 
-	GcovCoverage               *bool    `json:",omitempty"`
-	ClangCoverage              *bool    `json:",omitempty"`
-	NativeCoveragePaths        []string `json:",omitempty"`
-	NativeCoverageExcludePaths []string `json:",omitempty"`
+	GcovCoverage                *bool    `json:",omitempty"`
+	ClangCoverage               *bool    `json:",omitempty"`
+	NativeCoveragePaths         []string `json:",omitempty"`
+	NativeCoverageExcludePaths  []string `json:",omitempty"`
+	ClangCoverageContinuousMode *bool    `json:",omitempty"`
 
 	// Set by NewConfig
 	Native_coverage *bool `json:",omitempty"`
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 295b0e5..954f8d0 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"sort"
+	"strconv"
 	"strings"
 
 	mkparser "android/soong/androidmk/parser"
@@ -623,6 +624,16 @@
 	return err
 }
 
+// Assigns a given boolean value to a given variable in the result bp file. See
+// setVariable documentation for more information about prefix and name.
+func makeBlueprintBoolAssignment(ctx variableAssignmentContext, prefix, name string, value bool) error {
+	expressionValue, err := stringToBoolValue(strconv.FormatBool(value))
+	if err == nil {
+		err = setVariable(ctx.file, false, prefix, name, expressionValue, true)
+	}
+	return err
+}
+
 // If variable is a literal variable name, return the name, otherwise return ""
 func varLiteralName(variable mkparser.Variable) string {
 	if len(variable.Name.Variables) == 0 {
@@ -647,7 +658,11 @@
 	varname := ""
 	fixed := ""
 	val := ctx.mkvalue
+
 	if len(val.Variables) == 1 && varLiteralName(val.Variables[0]) != "" && len(val.Strings) == 2 && val.Strings[0] == "" {
+		if varLiteralName(val.Variables[0]) == "PRODUCT_OUT" && val.Strings[1] == "/system/priv-app" {
+			return makeBlueprintBoolAssignment(ctx, "", "privileged", true)
+		}
 		fixed = val.Strings[1]
 		varname = val.Variables[0].Name.Strings[0]
 		// TARGET_OUT_OPTIONAL_EXECUTABLES puts the artifact in xbin, which is
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index b8316a3..aaafdc7 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -411,6 +411,24 @@
 	return exp, nil
 }
 
+// If local is set to true, then the variable will be added as a part of the
+// variable at file.bpPos. For example, if file.bpPos references a module,
+// then calling this method will set a property on that module if local is set
+// to true. Otherwise, the Variable will be created at the root of the file.
+//
+// prefix should be populated with the top level value to be assigned, and
+// name with a sub-value. If prefix is empty, then name is the top level value.
+// For example, if prefix is "foo" and name is "bar" with a value of "baz", then
+// the following variable will be generated:
+//
+// foo {
+//   bar: "baz"
+// }
+//
+// If prefix is the empty string and name is "foo" with a value of "bar", the
+// following variable will be generated (if it is a property):
+//
+// foo: "bar"
 func setVariable(file *bpFile, plusequals bool, prefix, name string, value bpparser.Expression, local bool) error {
 	if prefix != "" {
 		name = prefix + "." + name
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index e8b6f78..2176361 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1675,6 +1675,21 @@
 }
 `,
 	},
+	{
+		desc: "privileged app",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/priv-app
+include $(BUILD_PACKAGE)
+		`,
+		expected: `
+android_app {
+	name: "foo",
+	privileged: true
+}
+`,
+	},
 }
 
 func TestEndToEnd(t *testing.T) {
diff --git a/androidmk/parser/make_strings.go b/androidmk/parser/make_strings.go
index aac4c4e..8030326 100644
--- a/androidmk/parser/make_strings.go
+++ b/androidmk/parser/make_strings.go
@@ -24,14 +24,24 @@
 // A MakeString is a string that may contain variable substitutions in it.
 // It can be considered as an alternating list of raw Strings and variable
 // substitutions, where the first and last entries in the list must be raw
-// Strings (possibly empty).  A MakeString that starts with a variable
-// will have an empty first raw string, and a MakeString that ends with a
-// variable will have an empty last raw string.  Two sequential Variables
-// will have an empty raw string between them.
+// Strings (possibly empty). The entirety of the text before the first variable,
+// between two variables, and after the last variable will be considered a
+// single String value. A MakeString that starts with a variable will have an
+// empty first raw string, and a MakeString that ends with a  variable will have
+// an empty last raw string.  Two sequential Variables will have an empty raw
+// string between them.
 //
 // The MakeString is stored as two lists, a list of raw Strings and a list
 // of Variables.  The raw string list is always one longer than the variable
 // list.
+//
+// For example, "$(FOO)/bar/baz" will be represented as the
+// following lists:
+//
+// {
+//   Strings: ["", "/bar/baz"],
+//   Variables: ["FOO"]
+// }
 type MakeString struct {
 	StringPos Pos
 	Strings   []string
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 8785ca0..059b4d7 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -373,8 +373,10 @@
 				}
 				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix)
 				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
-				fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
+				if a.installable() {
+					fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
+					fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
+				}
 
 				// Because apex writes .mk with Custom(), we need to write manually some common properties
 				// which are available via data.Entries
diff --git a/apex/builder.go b/apex/builder.go
index 183c215..8c5f99b 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -856,6 +856,10 @@
 		installSuffix = imageCapexSuffix
 	}
 
+	if !a.installable() {
+		a.SkipInstall()
+	}
+
 	// Install to $OUT/soong/{target,host}/.../apex.
 	a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
 		a.compatSymlinks.Paths()...)
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index b904c35..8a171d4 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -44,9 +44,16 @@
         "cc_library_shared_conversion_test.go",
         "cc_library_static_conversion_test.go",
         "cc_object_conversion_test.go",
+        "cc_prebuilt_library_shared_test.go",
         "conversion_test.go",
         "filegroup_conversion_test.go",
         "genrule_conversion_test.go",
+        "java_binary_host_conversion_test.go",
+        "java_import_conversion_test.go",
+        "java_library_conversion_test.go",
+        "java_library_host_conversion_test.go",
+        "java_plugin_conversion_test.go",
+        "java_proto_conversion_test.go",
         "performance_test.go",
         "prebuilt_etc_conversion_test.go",
         "python_binary_conversion_test.go",
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index 28de06c..42c1a54 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -51,6 +51,7 @@
 				"srcs":           `["app.java"]`,
 				"manifest":       `"AndroidManifest.xml"`,
 				"resource_files": `["res/res.png"]`,
+				"deps":           `["//prebuilts/sdk:public_current_android_sdk_java_import"]`,
 			}),
 		}})
 }
@@ -86,7 +87,10 @@
         "resb/res.png",
     ]`,
 				"custom_package": `"com.google"`,
-				"deps":           `[":static_lib_dep"]`,
+				"deps": `[
+        "//prebuilts/sdk:public_current_android_sdk_java_import",
+        ":static_lib_dep",
+    ]`,
 			}),
 		}})
 }
diff --git a/bp2build/java_proto_conversion_test.go b/bp2build/java_proto_conversion_test.go
index 93b0677..61a398c 100644
--- a/bp2build/java_proto_conversion_test.go
+++ b/bp2build/java_proto_conversion_test.go
@@ -71,8 +71,7 @@
 }`
 
 	protoLibrary := makeBazelTarget("proto_library", "java-protos_proto", attrNameToString{
-		"srcs":                `["a.proto"]`,
-		"strip_import_prefix": `""`,
+		"srcs": `["a.proto"]`,
 	})
 
 	for _, tc := range testCases {
@@ -107,8 +106,7 @@
 `,
 		expectedBazelTargets: []string{
 			makeBazelTarget("proto_library", "java-protos_proto", attrNameToString{
-				"srcs":                `["a.proto"]`,
-				"strip_import_prefix": `""`,
+				"srcs": `["a.proto"]`,
 			}),
 			makeBazelTarget(
 				"java_lite_proto_library",
diff --git a/cc/coverage.go b/cc/coverage.go
index f2b5425..d0902ea 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -77,6 +77,10 @@
 	return deps
 }
 
+func EnableContinuousCoverage(ctx android.BaseModuleContext) bool {
+	return ctx.DeviceConfig().ClangCoverageContinuousMode()
+}
+
 func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
 	clangCoverage := ctx.DeviceConfig().ClangCoverageEnabled()
 	gcovCoverage := ctx.DeviceConfig().GcovCoverageEnabled()
@@ -101,6 +105,9 @@
 			// Override -Wframe-larger-than.  We can expect frame size increase after
 			// coverage instrumentation.
 			flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=")
+			if EnableContinuousCoverage(ctx) {
+				flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-mllvm", "-runtime-counter-relocation")
+			}
 		}
 	}
 
@@ -152,6 +159,9 @@
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
 		} else if clangCoverage {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrFlag)
+			if EnableContinuousCoverage(ctx) {
+				flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm=-runtime-counter-relocation")
+			}
 
 			coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
 			deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 8a17e2e..e7c05ac 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -146,6 +146,7 @@
 	// binary flags
 	Symlinks         []string `json:",omitempty"`
 	StaticExecutable bool     `json:",omitempty"`
+	InstallInRoot    bool     `json:",omitempty"`
 
 	// dependencies
 	SharedLibs  []string `json:",omitempty"`
@@ -320,6 +321,7 @@
 			// binary flags
 			prop.Symlinks = m.Symlinks()
 			prop.StaticExecutable = m.StaticExecutable()
+			prop.InstallInRoot = m.InstallInRoot()
 			prop.SharedLibs = m.SnapshotSharedLibs()
 			// static libs dependencies are required to collect the NOTICE files.
 			prop.StaticLibs = m.SnapshotStaticLibs()
diff --git a/dexpreopt/DEXPREOPT_IMPLEMENTATION.md b/dexpreopt/DEXPREOPT_IMPLEMENTATION.md
new file mode 100644
index 0000000..c3a1730
--- /dev/null
+++ b/dexpreopt/DEXPREOPT_IMPLEMENTATION.md
@@ -0,0 +1,258 @@
+## Dexpreopt implementation
+
+### Introduction
+
+All dexpreopted Java code falls into three categories:
+
+- bootclasspath
+- system server
+- apps and libraries
+
+Dexpreopt implementation for bootclasspath libraries (boot images) is located in
+[soong/java] (see e.g. [soong/java/dexpreopt_bootjars.go]), and install rules
+are in [make/core/dex_preopt.mk].
+
+Dexpreopt implementation for system server, libraries and apps is located in
+[soong/dexpreopt]. For the rest of this section we focus primarily on it (and
+not boot images).
+
+Dexpeopt implementation is split across the Soong part and the Make part. The
+core logic is in Soong, and Make only generates configs and scripts to pass
+information to Soong.
+
+### Global and module dexpreopt.config
+
+The build system generates a global JSON dexpreopt config that is populated from
+product variables. This is static configuration that is passed to both Soong and
+Make. The `$OUT/soong/dexpreopt.config` file is generated in
+[make/core/dex_preopt_config.mk]. Soong reads it in [soong/dexpreopt/config.go]
+and makes a device-specific copy (this is needed to ensure incremental build
+correctness). The global config contains lists of bootclasspath jars, system
+server jars, dex2oat options, global switches that enable and disable parts of
+dexpreopt and so on.
+
+The build system also generates a module config for each dexpreopted package. It
+contains package-specific configuration that is derived from the global
+configuration and Android.bp or Android.mk module for the package.
+
+Module configs for Make packages are generated in
+[make/core/dex_preopt_odex_install.mk]; they are materialized as per-package
+JSON dexpreopt.config files.
+
+Module configs in Soong are not materialized as dexpreopt.config files and exist
+as Go structures in memory, unless it is necessary to materialize them as a file
+for dependent Make packages or for post-dexpreopting. Module configs are defined
+in [soong/dexpreopt/config.go].
+
+### Dexpreopt in Soong
+
+The Soong implementation of dexpreopt consists roughly of the following steps:
+
+- Read global dexpreopt config passed from Make ([soong/dexpreopt/config.go]).
+
+- Construct a static boot image config ([soong/java/dexpreopt_config.go]).
+
+- During dependency mutator pass, for each suitable module:
+    - add uses-library dependencies (e.g. for apps: [soong/java/app.go:deps])
+
+- During rule generation pass, for each suitable module:
+    - compute transitive uses-library dependency closure
+      ([soong/java/java.go:addCLCFromDep])
+
+    - construct CLC from the dependency closure
+      ([soong/dexpreopt/class_loader_context.go])
+
+    - construct module config with CLC, boot image locations, etc.
+      ([soong/java/dexpreopt.go])
+
+    - generate build rules to verify build-time CLC against the manifest (e.g.
+      for apps: [soong/java/app.go:verifyUsesLibraries])
+
+    - generate dexpreopt build rule ([soong/dexpreopt/dexpreopt.go])
+
+- At the end of rule generation pass:
+    - generate build rules for boot images ([soong/java/dexpreopt_bootjars.go],
+      [soong/java/bootclasspath_fragment.go] and
+      [soong/java/platform_bootclasspath.go])
+
+### Dexpreopt in Make - dexpreopt_gen
+
+In order to reuse the same dexpreopt implementation for both Soong and Make
+packages, part of Soong is compiled into a standalone binary dexpreopt_gen. It
+runs during the Ninja stage of the build and generates shell scripts with
+dexpreopt build rules for Make packages, and then executes them.
+
+This setup causes many inconveniences. To name a few:
+
+- Errors in the build rules are only revealed at the late stage of the build.
+
+- These rules are not tested by the presubmit builds that run `m nothing` on
+  many build targets/products.
+
+- It is impossible to find dexpreopt build rules in the generated Ninja files.
+
+However all these issues are a lesser evil compared to having a duplicate
+dexpreopt implementation in Make. Also note that it would be problematic to
+reimplement the logic in Make anyway, because Android.mk modules are not
+processed in the order of uses-library dependencies and propagating dependency
+information from one module to another would require a similar workaround with
+a script.
+
+Dexpreopt for Make packages involves a few steps:
+
+- At Soong phase (during `m nothing`), see dexpreopt_gen:
+    - generate build rules for dexpreopt_gen binary
+
+- At Make/Kati phase (during `m nothing`), see
+  [make/core/dex_preopt_odex_install.mk]:
+    - generate build rules for module dexpreopt.config
+
+    - generate build rules for merging dependency dexpreopt.config files (see
+      [make/core/dex_preopt_config_merger.py])
+
+    - generate build rules for dexpreopt_gen invocation
+
+    - generate build rules for executing dexpreopt.sh scripts
+
+- At Ninja phase (during `m`):
+    - generate dexpreopt.config files
+
+    - execute dexpreopt_gen rules (generate dexpreopt.sh scripts)
+
+    - execute dexpreopt.sh scripts (this runs the actual dexpreopt)
+
+The Make/Kati phase adds all the necessary dependencies that trigger
+dexpreopt_gen and dexpreopt.sh rules. The real dexpreopt command (dex2oat
+invocation that will be executed to AOT-compile a package) is in the
+dexpreopt.sh script, which is generated close to the end of the build.
+
+### Indirect build rules
+
+The process described above for Make packages involves "indirect build rules",
+i.e. build rules that are generated not at the time when the build system is
+created (which is a small step at the very beginning of the build triggered with
+`m nothing`), but at the time when the actual build is done (`m` phase).
+
+Some build systems, such as Make, allow modifications of the build graph during
+the build. Other build systems, such as Soong, have a clear separation into the
+first "generation phase" (this is when build rules are created) and the second
+"build phase" (this is when the build rules are executed), and they do not allow
+modifications of the dependency graph during the second phase. The Soong
+approach is better from performance standpoint, because with the Make approach
+there are no guarantees regarding the time of the build --- recursive build
+graph modfications continue until fixpoint. However the Soong approach is also
+more restictive, as it can only generate build rules from the information that
+is passed to the build system via global configuration, Android.bp files or
+encoded in the Go code. Any other information (such as the contents of the Java
+manifest files) are not accessible and cannot be used to generate build rules.
+
+Hence the need for the "indirect build rules": during the generation phase only
+stubs of the build rules are generated, and the real rules are generated by the
+stub rules during the build phase (and executed immediately). Note that the
+build system still has to add all the necessary dependencies during the
+generation phase, because it will not be possible to change build order during
+the build phase.
+
+Indirect buils rules are used in a couple of places in dexpreopt:
+
+- [soong/scripts/manifest_check.py]: first to extract targetSdkVersion from the
+  manifest, and later to extract `<uses-library/>` tags from the manifest and
+  compare them to the uses-library list known to the build system
+
+- [soong/scripts/construct_context.py]: to trim compatibility libraries in CLC
+
+- [make/core/dex_preopt_config_merger.py]: to merge information from
+  dexpreopt.config files for uses-library dependencies into the dependent's
+  dexpreopt.config file (mostly the CLC)
+
+- autogenerated dexpreopt.sh scripts: to call dexpreopt_gen
+
+### Consistency check - manifest_check.py
+
+Because the information from the manifests has to be duplicated in the
+Android.bp/Android.mk files, there is a danger that it may get out of sync. To
+guard against that, the build system generates a rule that verifies
+uses-libraries: checks the metadata in the build files against the contents of a
+manifest. The manifest can be available as a source file, or as part of a
+prebuilt APK.
+
+The check is implemented in [soong/scripts/manifest_check.py].
+
+It is possible to turn off the check globally for a product by setting
+`PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true` in a product makefile, or for a
+particular build by setting `RELAX_USES_LIBRARY_CHECK=true`.
+
+### Compatibility libraries - construct_context.py
+
+Compatibility libraries are libraries that didn’t exist prior to a certain SDK
+version (say, `N`), but classes in them were in the bootclasspath jars, etc.,
+and in version `N` they have been separated into a standalone uses-library.
+Compatibility libraries should only be in the CLC of an app if its
+`targetSdkVersion` in the manifest is less than `N`.
+
+Currently compatibility libraries only affect apps (but not other libraries).
+
+The build system cannot see `targetSdkVersion` of an app at the time it
+generates dexpreopt build rules, so it doesn't know whether to add compatibility
+libaries to CLC or not. As a workaround, the build system includes all
+compatibility libraries regardless of the app version, and appends some extra
+logic to the dexpreopt rule that will extract `targetSdkVersion` from the
+manifest and filter CLC based on that version during Ninja stage of the build,
+immediately before executing the dexpreopt command (see the
+soong/scripts/construct_context.py script).
+
+As of the time of writing (January 2022), there are the following compatibility
+libraries:
+
+- org.apache.http.legacy (SDK 28)
+- android.hidl.base-V1.0-java (SDK 29)
+- android.hidl.manager-V1.0-java (SDK 29)
+- android.test.base (SDK 30)
+- android.test.mock (SDK 30)
+
+### Manifest fixer
+
+Sometimes uses-library tags are missing from the source manifest of a
+library/app. This may happen for example if one of the transitive dependencies
+of the library/app starts using another uses-library, and the library/app's
+manifest isn't updated to include it.
+
+Soong can compute some of the missing uses-library tags for a given library/app
+automatically as SDK libraries in the transitive dependency closure of the
+library/app. The closure is needed because a library/app may depend on a static
+library that may in turn depend on an SDK library (possibly transitively via
+another library).
+
+Not all uses-library tags can be computed in this way, because some of the
+uses-library dependencies are not SDK libraries, or they are not reachable via
+transitive dependency closure. But when possible, allowing Soong to calculate
+the manifest entries is less prone to errors and simplifies maintenance. For
+example, consider a situation when many apps use some static library that adds a
+new uses-library dependency -- all the apps will have to be updated. That is
+difficult to maintain.
+
+There is also a manifest merger, because sometimes the final manifest of an app
+is merged from a few dependency manifests, so the final manifest installed on
+devices contains a superset of uses-library tags of the source manifest of the
+app.
+
+
+[make/core/dex_preopt.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt.mk
+[make/core/dex_preopt_config.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_config.mk
+[make/core/dex_preopt_config_merger.py]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_config_merger.py
+[make/core/dex_preopt_odex_install.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_odex_install.mk
+[soong/dexpreopt]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt
+[soong/dexpreopt/class_loader_context.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/class_loader_context.go
+[soong/dexpreopt/config.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/config.go
+[soong/dexpreopt/dexpreopt.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/dexpreopt.go
+[soong/java]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java
+[soong/java/app.go:deps]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/app.go?q=%22func%20\(u%20*usesLibrary\)%20deps%22
+[soong/java/app.go:verifyUsesLibraries]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/app.go?q=%22func%20\(u%20*usesLibrary\)%20verifyUsesLibraries%22
+[soong/java/bootclasspath_fragment.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/bootclasspath_fragment.go
+[soong/java/dexpreopt.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt.go
+[soong/java/dexpreopt_bootjars.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt_bootjars.go
+[soong/java/dexpreopt_config.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt_config.go
+[soong/java/java.go:addCLCFromDep]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/java.go?q=%22func%20addCLCfromDep%22
+[soong/java/platform_bootclasspath.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/platform_bootclasspath.go
+[soong/scripts/construct_context.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/scripts/construct_context.py
+[soong/scripts/manifest_check.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/scripts/manifest_check.py
diff --git a/docs/rbe.json b/docs/rbe.json
new file mode 100644
index 0000000..f6ff107
--- /dev/null
+++ b/docs/rbe.json
@@ -0,0 +1,24 @@
+{
+    "env": {
+        "USE_RBE": "1",
+
+        "RBE_R8_EXEC_STRATEGY": "remote_local_fallback",
+        "RBE_CXX_EXEC_STRATEGY": "remote_local_fallback",
+        "RBE_D8_EXEC_STRATEGY": "remote_local_fallback",
+        "RBE_JAVAC_EXEC_STRATEGY": "remote_local_fallback",
+        "RBE_JAVAC": "1",
+        "RBE_R8": "1",
+        "RBE_D8": "1",
+
+        "RBE_instance": "[replace with your RBE instance]",
+        "RBE_service": "[replace with your RBE service endpoint]",
+
+        "RBE_DIR": "prebuilts/remoteexecution-client/live",
+
+        "RBE_use_application_default_credentials": "true",
+
+        "RBE_log_dir": "/tmp",
+        "RBE_output_dir": "/tmp",
+        "RBE_proxy_log_dir": "/tmp"
+    }
+}
diff --git a/docs/rbe.md b/docs/rbe.md
new file mode 100644
index 0000000..cfe86d7
--- /dev/null
+++ b/docs/rbe.md
@@ -0,0 +1,70 @@
+# Build Android Platform on Remote Build Execution
+
+Soong is integrated with Google's Remote Build Execution(RBE) service, which
+implements the
+[Remote Executaion API](https://github.com/bazelbuild/remote-apis).
+
+With RBE enabled, it can speed up the Android Platform builds by distributing
+build actions through a worker pool sharing a central cache of build results.
+
+## Configuration
+
+To enable RBE, you need to set several environment variables before triggering
+the build. You can set them through a
+[environment variables config file](https://android.googlesource.com/platform/build/soong/+/master/README.md#environment-variables-config-file).
+As an example, [build/soong/docs/rbe.json](rbe.json) is a config that enables
+RBE in the build. Once the config file is created, you need to let Soong load
+the config file by specifying `ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR` environment
+variable and `ANDROID_BUILD_ENVIRONMENT_CONFIG` environment variable. The
+following command starts Soong with [build/soong/docs/rbe.json](rbe.json)
+loaded:
+
+```shell
+ANDROID_BUILD_ENVIRONMENT_CONFIG=rbe \
+ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR=build/soong/doc \
+  build/soong/soong_ui.bash
+```
+
+### Configuration Explanation
+
+Below a brief explanation of each field in
+[build/soong/docs/rbe.json](rbe.json):
+
+##### USE\_RBE:
+If set to 1, enable RBE for the build.
+
+##### RBE\_CXX\_EXEC\_STRATEGY / RBE\_JAVAC\_EXEC\_STRATEGY / RBE\_R8\_EXEC\_STRATEGY / RBE\_D8\_EXEC\_STRATEGY:
+
+Sets strategies for C++/javac/r8/d8 action types. Available options are
+(**Note**: all options will update the remote cache if the right permissions to
+update cache are given to the user.):
+
+*   **local**: Only execute locally.
+*   **remote**: Only execute remotely.
+*   **remote_local_fallback**: Try executing remotely and fall back to local
+    execution if failed.
+*   **racing**: Race remote execution and local execution and use the earlier
+    result.
+
+##### RBE\_JAVAC / RBE\_R8 / RBE\_D8
+
+If set to 1, enable javac/r8/d8 support. C++ compilation is enabled by default.
+
+##### RBE\_service / RBE\_instance
+
+The remote execution service endpoint and instance ID to target when calling
+remote execution via gRPC to execute actions.
+
+##### RBE\_DIR
+
+Where to find remote client binaries (rewrapper, reproxy)
+
+##### RBE\_use\_application\_default\_credentials
+
+reclient uses
+[application default credentials](https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login)
+for autentication, as generated by `gcloud auth application-default login`
+
+##### RBE\_log\_dir/RBE\_proxy\_log\_dir/RBE\_output\_dir
+
+Logs generated by rewrapper and reproxy will go here.
diff --git a/java/app.go b/java/app.go
index e4432ff..96fd61a 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1443,7 +1443,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "android_app_certificate",
-		Bzl_load_location: "//build/bazel/rules:android_app_certificate.bzl",
+		Bzl_load_location: "//build/bazel/rules/android:android_app_certificate.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
@@ -1451,9 +1451,11 @@
 
 type bazelAndroidAppAttributes struct {
 	*javaLibraryAttributes
-	Manifest       bazel.Label
-	Custom_package *string
-	Resource_files bazel.LabelListAttribute
+	Manifest         bazel.Label
+	Custom_package   *string
+	Resource_files   bazel.LabelListAttribute
+	Certificate      *bazel.Label
+	Certificate_name *string
 }
 
 // ConvertWithBp2build is used to convert android_app to Bazel.
@@ -1470,15 +1472,30 @@
 		resourceFiles.Includes = append(resourceFiles.Includes, files...)
 	}
 
+	var certificate *bazel.Label
+	certificateNamePtr := a.overridableAppProperties.Certificate
+	certificateName := proptools.StringDefault(certificateNamePtr, "")
+	certModule := android.SrcIsModule(certificateName)
+	if certModule != "" {
+		c := android.BazelLabelForModuleDepSingle(ctx, certificateName)
+		certificate = &c
+		certificateNamePtr = nil
+	}
+
 	attrs := &bazelAndroidAppAttributes{
 		libAttrs,
 		android.BazelLabelForModuleSrcSingle(ctx, manifest),
 		// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
 		a.overridableAppProperties.Package_name,
 		bazel.MakeLabelListAttribute(resourceFiles),
+		certificate,
+		certificateNamePtr,
 	}
-	props := bazel.BazelTargetModuleProperties{Rule_class: "android_binary",
-		Bzl_load_location: "@rules_android//rules:rules.bzl"}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "android_binary",
+		Bzl_load_location: "//build/bazel/rules/android:android_binary.bzl",
+	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
 
diff --git a/java/base.go b/java/base.go
index 8747039..9978a66 100644
--- a/java/base.go
+++ b/java/base.go
@@ -481,6 +481,8 @@
 	sdkVersion    android.SdkSpec
 	minSdkVersion android.SdkSpec
 	maxSdkVersion android.SdkSpec
+
+	sourceExtensions []string
 }
 
 func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
@@ -982,6 +984,14 @@
 	return flags
 }
 
+func (j *Module) AddJSONData(d *map[string]interface{}) {
+	(&j.ModuleBase).AddJSONData(d)
+	(*d)["Java"] = map[string]interface{}{
+		"SourceExtensions": j.sourceExtensions,
+	}
+
+}
+
 func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
 
@@ -993,6 +1003,12 @@
 	}
 
 	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
+	j.sourceExtensions = []string{}
+	for _, ext := range []string{".kt", ".proto", ".aidl", ".java", ".logtags"} {
+		if hasSrcExt(srcFiles.Strings(), ext) {
+			j.sourceExtensions = append(j.sourceExtensions, ext)
+		}
+	}
 	if hasSrcExt(srcFiles.Strings(), ".proto") {
 		flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags)
 	}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 5fe409e..eddcb61 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"io"
 	"path/filepath"
 	"reflect"
 	"strings"
@@ -588,6 +589,19 @@
 			// Provide the apex content info.
 			b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFilesByArch)
 		}
+	} else {
+		// Versioned fragments are not needed by make.
+		b.HideFromMake()
+	}
+
+	// In order for information about bootclasspath_fragment modules to be added to module-info.json
+	// it is necessary to output an entry to Make. As bootclasspath_fragment modules are part of an
+	// APEX there can be multiple variants, including the default/platform variant and only one can
+	// be output to Make but it does not really matter which variant is output. The default/platform
+	// variant is the first (ctx.PrimaryModule()) and is usually hidden from make so this just picks
+	// the last variant (ctx.FinalModule()).
+	if ctx.Module() != ctx.FinalModule() {
+		b.HideFromMake()
 	}
 }
 
@@ -849,7 +863,22 @@
 }
 
 func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries {
-	var entriesList []android.AndroidMkEntries
+	// Use the generated classpath proto as the output.
+	outputFile := b.outputFilepath
+	// Create a fake entry that will cause this to be added to the module-info.json file.
+	entriesList := []android.AndroidMkEntries{{
+		Class:      "FAKE",
+		OutputFile: android.OptionalPathForPath(outputFile),
+		Include:    "$(BUILD_PHONY_PACKAGE)",
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string) {
+				// Allow the bootclasspath_fragment to be built by simply passing its name on the command
+				// line.
+				fmt.Fprintln(w, ".PHONY:", b.Name())
+				fmt.Fprintln(w, b.Name()+":", outputFile.String())
+			},
+		},
+	}}
 	for _, install := range b.bootImageDeviceInstalls {
 		entriesList = append(entriesList, install.ToMakeEntries())
 	}
diff --git a/java/config/config.go b/java/config/config.go
index 39584cb..05dfde6 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -105,7 +105,12 @@
 		if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" {
 			return override
 		}
-		return "11"
+		switch ctx.Config().Getenv("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") {
+		case "true":
+			return "17"
+		default:
+			return "11"
+		}
 	})
 
 	pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 5dc7bc9..272cf1e 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -334,7 +334,11 @@
 		// TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
 		cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
 			FlagWithArg("--hide ", "SuperfluousPrefix").
-			FlagWithArg("--hide ", "AnnotationExtraction")
+			FlagWithArg("--hide ", "AnnotationExtraction").
+			// b/222738070
+			FlagWithArg("--hide ", "BannedThrow").
+			// b/223382732
+			FlagWithArg("--hide ", "ChangedDefault")
 	}
 }
 
diff --git a/java/java.go b/java/java.go
index d0f0abc..0a35908 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2030,6 +2030,11 @@
 	}
 
 	var deps bazel.LabelList
+	sdkVersion := m.SdkVersion(ctx)
+	if sdkVersion.Kind == android.SdkPublic && sdkVersion.ApiLevel == android.FutureApiLevel {
+		// TODO(b/220869005) remove forced dependency on current public android.jar
+		deps.Add(&bazel.Label{Label: "//prebuilts/sdk:public_current_android_sdk_java_import"})
+	}
 	if m.properties.Libs != nil {
 		deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Libs))
 	}
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 74053fe..d108a0d 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -465,17 +465,17 @@
 	return ctx
 }
 
-func (ctx *parseContext) lastAssignment(name string) *assignmentNode {
+func (ctx *parseContext) lastAssignment(v variable) *assignmentNode {
 	for va := ctx.varAssignments; va != nil; va = va.outer {
-		if v, ok := va.vars[name]; ok {
+		if v, ok := va.vars[v.name()]; ok {
 			return v
 		}
 	}
 	return nil
 }
 
-func (ctx *parseContext) setLastAssignment(name string, asgn *assignmentNode) {
-	ctx.varAssignments.vars[name] = asgn
+func (ctx *parseContext) setLastAssignment(v variable, asgn *assignmentNode) {
+	ctx.varAssignments.vars[v.name()] = asgn
 }
 
 func (ctx *parseContext) pushVarAssignments() {
@@ -532,7 +532,7 @@
 	if lhs == nil {
 		return []starlarkNode{ctx.newBadNode(a, "unknown variable %s", name)}
 	}
-	_, isTraced := ctx.tracedVariables[name]
+	_, isTraced := ctx.tracedVariables[lhs.name()]
 	asgn := &assignmentNode{lhs: lhs, mkValue: a.Value, isTraced: isTraced, location: ctx.errorLocation(a)}
 	if lhs.valueType() == starlarkTypeUnknown {
 		// Try to divine variable type from the RHS
@@ -565,8 +565,8 @@
 		}
 	}
 
-	asgn.previous = ctx.lastAssignment(name)
-	ctx.setLastAssignment(name, asgn)
+	asgn.previous = ctx.lastAssignment(lhs)
+	ctx.setLastAssignment(lhs, asgn)
 	switch a.Type {
 	case "=", ":=":
 		asgn.flavor = asgnSet
@@ -1268,12 +1268,12 @@
 				args: []starlarkExpr{
 					&stringLiteralExpr{literal: substParts[0]},
 					&stringLiteralExpr{literal: substParts[1]},
-					NewVariableRefExpr(v, ctx.lastAssignment(v.name()) != nil),
+					NewVariableRefExpr(v, ctx.lastAssignment(v) != nil),
 				},
 			}
 		}
 		if v := ctx.addVariable(refDump); v != nil {
-			return NewVariableRefExpr(v, ctx.lastAssignment(v.name()) != nil)
+			return NewVariableRefExpr(v, ctx.lastAssignment(v) != nil)
 		}
 		return ctx.newBadExpr(node, "unknown variable %s", refDump)
 	}
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index 55d48ff..556dcaa 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -65,6 +65,10 @@
 PRODUCT_NAME := Pixel 3
 PRODUCT_MODEL :=
 local_var = foo
+local-var-with-dashes := bar
+$(warning local-var-with-dashes: $(local-var-with-dashes))
+GLOBAL-VAR-WITH-DASHES := baz
+$(warning GLOBAL-VAR-WITH-DASHES: $(GLOBAL-VAR-WITH-DASHES))
 `,
 		expected: `load("//build/make/core:product_config.rbc", "rblf")
 
@@ -73,6 +77,10 @@
   cfg["PRODUCT_NAME"] = "Pixel 3"
   cfg["PRODUCT_MODEL"] = ""
   _local_var = "foo"
+  _local_var_with_dashes = "bar"
+  rblf.mkwarning("pixel3.mk", "local-var-with-dashes: %s" % _local_var_with_dashes)
+  g["GLOBAL-VAR-WITH-DASHES"] = "baz"
+  rblf.mkwarning("pixel3.mk", "GLOBAL-VAR-WITH-DASHES: %s" % g["GLOBAL-VAR-WITH-DASHES"])
 `,
 	},
 	{
@@ -985,6 +993,7 @@
 def init(g, handle):
   cfg = rblf.cfg(handle)
   if "hwaddress" not in cfg.get("PRODUCT_PACKAGES", []):
+    rblf.setdefault(handle, "PRODUCT_PACKAGES")
     cfg["PRODUCT_PACKAGES"] = (rblf.mkstrip("%s hwaddress" % " ".join(cfg.get("PRODUCT_PACKAGES", [])))).split()
 `,
 	},
diff --git a/mk2rbc/variable.go b/mk2rbc/variable.go
index f7adca5..6805744 100644
--- a/mk2rbc/variable.go
+++ b/mk2rbc/variable.go
@@ -88,20 +88,33 @@
 		}
 		value.emit(gctx)
 	}
-
-	switch asgn.flavor {
-	case asgnSet:
-		emitAssignment()
-	case asgnAppend:
-		emitAppend()
-	case asgnMaybeAppend:
-		// If we are not sure variable has been assigned before, emit setdefault
+	emitSetDefault := func() {
 		if pcv.typ == starlarkTypeList {
 			gctx.writef("%s(handle, %q)", cfnSetListDefault, pcv.name())
 		} else {
 			gctx.writef("cfg.setdefault(%q, %s)", pcv.name(), pcv.defaultValueString())
 		}
 		gctx.newLine()
+	}
+
+	switch asgn.flavor {
+	case asgnSet:
+		isSelfReferential := false
+		asgn.value.transform(func(expr starlarkExpr) starlarkExpr {
+			if ref, ok := expr.(*variableRefExpr); ok && ref.ref.name() == pcv.name() {
+				isSelfReferential = true
+			}
+			return nil
+		})
+		if isSelfReferential {
+			emitSetDefault()
+		}
+		emitAssignment()
+	case asgnAppend:
+		emitAppend()
+	case asgnMaybeAppend:
+		// If we are not sure variable has been assigned before, emit setdefault
+		emitSetDefault()
 		emitAppend()
 	case asgnMaybeSet:
 		gctx.writef("if cfg.get(%q) == None:", pcv.nam)
@@ -278,6 +291,14 @@
 // addVariable returns a variable with a given name. A variable is
 // added if it does not exist yet.
 func (ctx *parseContext) addVariable(name string) variable {
+	// Heuristics: if variable's name is all lowercase, consider it local
+	// string variable.
+	isLocalVariable := name == strings.ToLower(name)
+	// Local variables can't have special characters in them, because they
+	// will be used as starlark identifiers
+	if isLocalVariable {
+		name = strings.ReplaceAll(strings.TrimSpace(name), "-", "_")
+	}
 	v, found := ctx.variables[name]
 	if !found {
 		_, preset := presetVariables[name]
@@ -288,9 +309,7 @@
 			case VarClassSoong:
 				v = &otherGlobalVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
 			}
-		} else if name == strings.ToLower(name) {
-			// Heuristics: if variable's name is all lowercase, consider it local
-			// string variable.
+		} else if isLocalVariable {
 			v = &localVariable{baseVariable{nam: name, typ: starlarkTypeUnknown}}
 		} else {
 			vt := starlarkTypeUnknown
diff --git a/rust/compiler.go b/rust/compiler.go
index c5d40f4..19499fa 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -121,6 +121,12 @@
 	// include all of the static libraries symbols in any dylibs or binaries which use this rlib as well.
 	Whole_static_libs []string `android:"arch_variant"`
 
+	// list of Rust system library dependencies.
+	//
+	// This is usually only needed when `no_stdlibs` is true, in which case it can be used to depend on system crates
+	// like `core` and `alloc`.
+	Stdlibs []string `android:"arch_variant"`
+
 	// crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider
 	// modules which create library variants (rust_bindgen). This must be the expected extern crate name used in
 	// source, and is required to conform to an enforced format matching library output files (if the output file is
@@ -360,6 +366,7 @@
 	deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
 	deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs...)
 	deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...)
+	deps.Stdlibs = append(deps.Stdlibs, compiler.Properties.Stdlibs...)
 
 	if !Bool(compiler.Properties.No_stdlibs) {
 		for _, stdlib := range config.Stdlibs {
diff --git a/rust/coverage.go b/rust/coverage.go
index 050b811..651ce6e 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -22,6 +22,7 @@
 
 var CovLibraryName = "libprofile-clang-extras"
 
+// Add '%c' to default specifier after we resolve http://b/210012154
 const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
 
 type coverage struct {
@@ -59,6 +60,10 @@
 		flags.LinkFlags = append(flags.LinkFlags,
 			profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
 		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+		if cc.EnableContinuousCoverage(ctx) {
+			flags.RustFlags = append(flags.RustFlags, "-C llvm-args=--runtime-counter-relocation")
+			flags.LinkFlags = append(flags.LinkFlags, "-Wl,-mllvm,-runtime-counter-relocation")
+		}
 	}
 
 	return flags, deps
diff --git a/rust/image.go b/rust/image.go
index 5d57f15..dfc7f74 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -149,6 +149,10 @@
 	return mod.ModuleBase.InRecovery() || mod.ModuleBase.InstallInRecovery()
 }
 
+func (mod *Module) InRamdisk() bool {
+	return mod.ModuleBase.InRamdisk() || mod.ModuleBase.InstallInRamdisk()
+}
+
 func (mod *Module) InVendorRamdisk() bool {
 	return mod.ModuleBase.InVendorRamdisk() || mod.ModuleBase.InstallInVendorRamdisk()
 }
diff --git a/rust/rust.go b/rust/rust.go
index f40f1a8..1c718a4 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -968,6 +968,7 @@
 	deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
 	deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
 	deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
+	deps.Stdlibs = android.LastUniqueStrings(deps.Stdlibs)
 	deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
 	return deps
 
diff --git a/ui/build/config.go b/ui/build/config.go
index 077a4d1..01fe8fa 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -368,10 +368,14 @@
 	java8Home := filepath.Join("prebuilts/jdk/jdk8", ret.HostPrebuiltTag())
 	java9Home := filepath.Join("prebuilts/jdk/jdk9", ret.HostPrebuiltTag())
 	java11Home := filepath.Join("prebuilts/jdk/jdk11", ret.HostPrebuiltTag())
+	java17Home := filepath.Join("prebuilts/jdk/jdk17", ret.HostPrebuiltTag())
 	javaHome := func() string {
 		if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok {
 			return override
 		}
+		if ret.environ.IsEnvTrue("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") {
+			return java17Home
+		}
 		if toolchain11, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN"); ok && toolchain11 != "true" {
 			ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN is no longer supported. An OpenJDK 11 toolchain is now the global default.")
 		}
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 81c500d..831a80f 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -91,6 +91,7 @@
 	"pstree":  Allowed,
 	"rsync":   Allowed,
 	"sh":      Allowed,
+	"stubby":  Allowed,
 	"tr":      Allowed,
 	"unzip":   Allowed,
 	"zip":     Allowed,