Merge "Always use RELR for Rust."
diff --git a/android/config.go b/android/config.go
index 54d47cf..3dae6e2 100644
--- a/android/config.go
+++ b/android/config.go
@@ -35,7 +35,8 @@
 
 var Bool = proptools.Bool
 var String = proptools.String
-var FutureApiLevel = 10000
+
+const FutureApiLevel = 10000
 
 // The configuration file name
 const configFileName = "soong.config"
@@ -815,6 +816,18 @@
 	return Bool(c.productVariables.UseRBE)
 }
 
+func (c *config) UseRBEJAVAC() bool {
+	return Bool(c.productVariables.UseRBEJAVAC)
+}
+
+func (c *config) UseRBER8() bool {
+	return Bool(c.productVariables.UseRBER8)
+}
+
+func (c *config) UseRBED8() bool {
+	return Bool(c.productVariables.UseRBED8)
+}
+
 func (c *config) UseRemoteBuild() bool {
 	return c.UseGoma() || c.UseRBE()
 }
diff --git a/android/hooks.go b/android/hooks.go
index e8cd81b..47f69d1 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -97,7 +97,7 @@
 			module.base().variableProperties,
 			// Put an empty copy of the src properties into dst so that properties in src that are not in dst
 			// don't cause a "failed to find property to extend" error.
-			proptools.CloneEmptyProperties(reflect.ValueOf(src).Elem()).Interface(),
+			proptools.CloneEmptyProperties(reflect.ValueOf(src)).Interface(),
 		}
 		err := proptools.AppendMatchingProperties(dst, src, nil)
 		if err != nil {
diff --git a/android/mutator.go b/android/mutator.go
index 3d1bb4f..a46d4be 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -270,7 +270,7 @@
 			module.base().variableProperties,
 			// Put an empty copy of the src properties into dst so that properties in src that are not in dst
 			// don't cause a "failed to find property to extend" error.
-			proptools.CloneEmptyProperties(reflect.ValueOf(src).Elem()).Interface(),
+			proptools.CloneEmptyProperties(reflect.ValueOf(src)).Interface(),
 		}
 		err := proptools.AppendMatchingProperties(dst, src, nil)
 		if err != nil {
diff --git a/android/package_ctx.go b/android/package_ctx.go
index 6350667..5a43ea9 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -232,16 +232,32 @@
 	}, argNames...)
 }
 
-// RemoteRuleSupports selects if a AndroidRemoteStaticRule supports goma, RBE, or both.
-type RemoteRuleSupports int
+// RBEExperimentalFlag indicates which flag should be set for the AndroidRemoteStaticRule
+// to use RBE.
+type RBEExperimentalFlag int
 
 const (
-	SUPPORTS_NONE = 0
-	SUPPORTS_GOMA = 1 << iota
-	SUPPORTS_RBE  = 1 << iota
-	SUPPORTS_BOTH = SUPPORTS_GOMA | SUPPORTS_RBE
+	// RBE_NOT_EXPERIMENTAL indicates the rule should use RBE in every build that has
+	// UseRBE set.
+	RBE_NOT_EXPERIMENTAL RBEExperimentalFlag = iota
+	// RBE_JAVAC indicates the rule should use RBE only if the RBE_JAVAC variable is
+	// set in an RBE enabled build.
+	RBE_JAVAC
+	// RBE_R8 indicates the rule should use RBE only if the RBE_R8 variable is set in
+	// an RBE enabled build.
+	RBE_R8
+	// RBE_D8 indicates the rule should use RBE only if the RBE_D8 variable is set in
+	// an RBE enabled build.
+	RBE_D8
 )
 
+// RemoteRuleSupports configures rules with whether they have Goma and/or RBE support.
+type RemoteRuleSupports struct {
+	Goma    bool
+	RBE     bool
+	RBEFlag RBEExperimentalFlag
+}
+
 // AndroidRemoteStaticRule wraps blueprint.StaticRule but uses goma or RBE's parallelism if goma or RBE are enabled
 // and the appropriate SUPPORTS_* flag is set.
 func (p PackageContext) AndroidRemoteStaticRule(name string, supports RemoteRuleSupports, params blueprint.RuleParams,
@@ -249,18 +265,30 @@
 
 	return p.PackageContext.RuleFunc(name, func(config interface{}) (blueprint.RuleParams, error) {
 		ctx := &configErrorWrapper{p, config.(Config), nil}
-		if ctx.Config().UseGoma() && supports&SUPPORTS_GOMA == 0 {
+		if ctx.Config().UseGoma() && !supports.Goma {
 			// When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the
 			// local parallelism value
 			params.Pool = localPool
 		}
 
-		if ctx.Config().UseRBE() && supports&SUPPORTS_RBE == 0 {
+		if ctx.Config().UseRBE() && !supports.RBE {
 			// When USE_RBE=true is set and the rule is not supported by RBE, restrict jobs to the
 			// local parallelism value
 			params.Pool = localPool
 		}
 
+		if ctx.Config().UseRBE() && supports.RBE {
+			if supports.RBEFlag == RBE_JAVAC && !ctx.Config().UseRBEJAVAC() {
+				params.Pool = localPool
+			}
+			if supports.RBEFlag == RBE_R8 && !ctx.Config().UseRBER8() {
+				params.Pool = localPool
+			}
+			if supports.RBEFlag == RBE_D8 && !ctx.Config().UseRBED8() {
+				params.Pool = localPool
+			}
+		}
+
 		return params, nil
 	}, argNames...)
 }
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 928ba53..b4f144a 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -417,10 +417,10 @@
 	}
 
 	var pool blueprint.Pool
-	if ctx.Config().UseGoma() && r.remoteable&SUPPORTS_GOMA != 0 {
+	if ctx.Config().UseGoma() && r.remoteable.Goma {
 		// When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool.
-	} else if ctx.Config().UseRBE() && r.remoteable&SUPPORTS_RBE != 0 {
-		// When USE_GOMA=true is set and the rule is supported by RBE, allow jobs to run outside the local pool.
+	} else if ctx.Config().UseRBE() && r.remoteable.RBE {
+		// When USE_RBE=true is set and the rule is supported by RBE, allow jobs to run outside the local pool.
 	} else if r.highmem {
 		pool = highmemPool
 	} else if ctx.Config().UseRemoteBuild() {
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index bdec64b..3e55958 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -351,7 +351,7 @@
 		return func() (blueprint.Module, []interface{}) {
 			module, props := factory()
 
-			conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps.Elem())
+			conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
 			props = append(props, conditionalProps.Interface())
 
 			AddLoadHook(module, func(ctx LoadHookContext) {
diff --git a/android/variable.go b/android/variable.go
index 7473491..51306ad 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -43,8 +43,10 @@
 		} `android:"arch_variant"`
 
 		Malloc_not_svelte struct {
-			Cflags      []string `android:"arch_variant"`
-			Shared_libs []string `android:"arch_variant"`
+			Cflags              []string `android:"arch_variant"`
+			Shared_libs         []string `android:"arch_variant"`
+			Whole_static_libs   []string `android:"arch_variant"`
+			Exclude_static_libs []string `android:"arch_variant"`
 		} `android:"arch_variant"`
 
 		Safestack struct {
@@ -204,6 +206,9 @@
 	Binder32bit                      *bool `json:",omitempty"`
 	UseGoma                          *bool `json:",omitempty"`
 	UseRBE                           *bool `json:",omitempty"`
+	UseRBEJAVAC                      *bool `json:",omitempty"`
+	UseRBER8                         *bool `json:",omitempty"`
+	UseRBED8                         *bool `json:",omitempty"`
 	Debuggable                       *bool `json:",omitempty"`
 	Eng                              *bool `json:",omitempty"`
 	Treble_linker_namespaces         *bool `json:",omitempty"`
diff --git a/android/variable_test.go b/android/variable_test.go
index 751677f..cde2b1a 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -159,15 +159,15 @@
 func TestProductVariables(t *testing.T) {
 	ctx := NewTestContext()
 	// A module type that has a srcs property but not a cflags property.
-	ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(struct {
+	ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(&struct {
 		Srcs []string
 	}{}))
 	// A module type that has a cflags property but not a srcs property.
-	ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(struct {
+	ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(&struct {
 		Cflags []string
 	}{}))
 	// A module type that does not have any properties that match product_variables.
-	ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(struct {
+	ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct {
 		Foo []string
 	}{}))
 	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 5fa5bf0..8929910 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -112,6 +112,11 @@
 			}
 		} else {
 			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
+
+			// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
+			// We don't need to have notice file for the individual modules in it. Otherwise,
+			// we will have duplicated notice entries.
+			fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
 		}
 		fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
 		fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.NameInMake())
@@ -158,6 +163,7 @@
 			fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false")
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
 		} else if fi.class == app {
+			fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", fi.certificate.AndroidMkString())
 			// soong_app_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .apk  Therefore
 			// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
 			// we will have foo.apk.apk
@@ -177,7 +183,7 @@
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
 		} else {
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
-			if fi.builtFile == a.manifestPbOut {
+			if fi.builtFile == a.manifestPbOut && apexType == flattenedApex {
 				if a.primaryApexType {
 					// Make apex_manifest.pb module for this APEX to override all other
 					// modules in the APEXes being overridden by this APEX
@@ -187,7 +193,7 @@
 					}
 					fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(patterns, " "))
 
-					if apexType == flattenedApex && len(a.compatSymlinks) > 0 {
+					if len(a.compatSymlinks) > 0 {
 						// For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
 						postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
 					}
@@ -271,6 +277,11 @@
 				if len(postInstallCommands) > 0 {
 					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
 				}
+
+				if a.mergedNotices.Merged.Valid() {
+					fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNotices.Merged.Path().String())
+				}
+
 				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 
 				if apexType == imageApex {
diff --git a/apex/apex.go b/apex/apex.go
index f925066..48cdedf 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -709,7 +709,8 @@
 	targetRequiredModuleNames []string
 	hostRequiredModuleNames   []string
 
-	jacocoReportClassesFile android.Path // only for javalibs and apps
+	jacocoReportClassesFile android.Path     // only for javalibs and apps
+	certificate             java.Certificate // only for apps
 }
 
 func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, moduleName string, installDir string, class apexFileClass, module android.Module) apexFile {
@@ -818,6 +819,9 @@
 	// Whether to create symlink to the system file instead of having a file
 	// inside the apex or not
 	linkToSystemLib bool
+
+	// Struct holding the merged notice file paths in different formats
+	mergedNotices android.NoticeOutputs
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -1195,6 +1199,7 @@
 	Privileged() bool
 	OutputFile() android.Path
 	JacocoReportClassesFile() android.Path
+	Certificate() java.Certificate
 }, pkgName string) apexFile {
 	appDir := "app"
 	if aapp.Privileged() {
@@ -1204,6 +1209,7 @@
 	fileToCopy := aapp.OutputFile()
 	af := newApexFile(ctx, fileToCopy, aapp.Name(), dirInApex, app, aapp)
 	af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
+	af.certificate = aapp.Certificate()
 	return af
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index b7dd7fb..c7ecbc9 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3014,7 +3014,7 @@
 	ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv/AppFooPriv.apk")
 
 	// JNI libraries are embedded inside APK
-	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_myapex").Rule("zip")
+	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_myapex").Description("zip jni lib")
 	libjniOutput := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_shared_myapex").Module().(*cc.Module).OutputFile()
 	ensureListContains(t, appZipRule.Implicits.Strings(), libjniOutput.String())
 	// ... uncompressed
diff --git a/apex/builder.go b/apex/builder.go
index 8ae2b5c..53f39a6 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -160,7 +160,7 @@
 			`echo -e "New unexpected files were added to ${apex_module_name}." ` +
 			` "To fix the build run following command:" && ` +
 			`echo "system/apex/tools/update_whitelist.sh ${whitelisted_files_file} ${image_content_file}" && ` +
-			`exit 1)`,
+			`exit 1); touch ${out}`,
 		Description: "Diff ${image_content_file} and ${whitelisted_files_file}",
 	}, "image_content_file", "whitelisted_files_file", "apex_module_name")
 )
@@ -211,7 +211,7 @@
 	})
 }
 
-func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext, apexFileName string) android.OptionalPath {
+func (a *apexBundle) buildNoticeFiles(ctx android.ModuleContext, apexFileName string) android.NoticeOutputs {
 	noticeFiles := []android.Path{}
 	for _, f := range a.filesInfo {
 		if f.module != nil {
@@ -227,10 +227,10 @@
 	}
 
 	if len(noticeFiles) == 0 {
-		return android.OptionalPath{}
+		return android.NoticeOutputs{}
 	}
 
-	return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles)).HtmlGzOutput
+	return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles))
 }
 
 func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.OutputPath {
@@ -394,11 +394,11 @@
 		optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion)
 		optFlags = append(optFlags, "--min_sdk_version "+minSdkVersion)
 
-		noticeFile := a.buildNoticeFile(ctx, a.Name()+suffix)
-		if noticeFile.Valid() {
+		a.mergedNotices = a.buildNoticeFiles(ctx, a.Name()+suffix)
+		if a.mergedNotices.HtmlGzOutput.Valid() {
 			// If there's a NOTICE file, embed it as an asset file in the APEX.
-			implicitInputs = append(implicitInputs, noticeFile.Path())
-			optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeFile.String()))
+			implicitInputs = append(implicitInputs, a.mergedNotices.HtmlGzOutput.Path())
+			optFlags = append(optFlags, "--assets_dir "+filepath.Dir(a.mergedNotices.HtmlGzOutput.String()))
 		}
 
 		if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && ctx.ModuleDir() != "system/apex/shim/build" && a.testOnlyShouldSkipHashtreeGeneration() {
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 024fcbc..1d792ef 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -33,7 +33,7 @@
 var (
 	pctx = android.NewPackageContext("android/soong/bpf")
 
-	ccRule = pctx.AndroidRemoteStaticRule("ccRule", android.SUPPORTS_GOMA,
+	ccRule = pctx.AndroidRemoteStaticRule("ccRule", android.RemoteRuleSupports{Goma: true},
 		blueprint.RuleParams{
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 4633aa6..0516279 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -120,6 +120,10 @@
 		Name: "removeEmptyLibDependencies",
 		Fix:  removeEmptyLibDependencies,
 	},
+	{
+		Name: "removeHidlInterfaceTypes",
+		Fix:  removeHidlInterfaceTypes,
+	},
 }
 
 func NewFixRequest() FixRequest {
@@ -698,6 +702,18 @@
 	return nil
 }
 
+// Removes hidl_interface 'types' which are no longer needed
+func removeHidlInterfaceTypes(f *Fixer) error {
+	for _, def := range f.tree.Defs {
+		mod, ok := def.(*parser.Module)
+		if !(ok && mod.Type == "hidl_interface") {
+			continue
+		}
+		removeProperty(mod, "types")
+	}
+	return nil
+}
+
 // Converts the default source list property, 'srcs', to a single source property with a given name.
 // "LOCAL_MODULE" reference is also resolved during the conversion process.
 func convertToSingleSource(mod *parser.Module, srcPropertyName string) {
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 032282f..38cefdd 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -887,3 +887,34 @@
 		})
 	}
 }
+
+func TestRemoveHidlInterfaceTypes(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "remove types",
+			in: `
+				hidl_interface {
+					name: "foo@1.0",
+					types: ["ParcelFooBar"],
+				}
+			`,
+			out: `
+				hidl_interface {
+					name: "foo@1.0",
+
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, func(fixer *Fixer) error {
+				return removeHidlInterfaceTypes(fixer)
+			})
+		})
+	}
+}
diff --git a/cc/builder.go b/cc/builder.go
index 5f0da5f..b05ca5b 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -46,7 +46,7 @@
 var (
 	pctx = android.NewPackageContext("android/soong/cc")
 
-	cc = pctx.AndroidRemoteStaticRule("cc", android.SUPPORTS_BOTH,
+	cc = pctx.AndroidRemoteStaticRule("cc", android.RemoteRuleSupports{Goma: true, RBE: true},
 		blueprint.RuleParams{
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
@@ -55,7 +55,7 @@
 		},
 		"ccCmd", "cFlags")
 
-	ccNoDeps = pctx.AndroidRemoteStaticRule("ccNoDeps", android.SUPPORTS_GOMA,
+	ccNoDeps = pctx.AndroidRemoteStaticRule("ccNoDeps", android.RemoteRuleSupports{Goma: true},
 		blueprint.RuleParams{
 			Command:     "$relPwd ${config.CcWrapper}$ccCmd -c $cFlags -o $out $in",
 			CommandDeps: []string{"$ccCmd"},
diff --git a/cc/config/global.go b/cc/config/global.go
index 87314dc..44de4d5 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -166,6 +166,8 @@
 			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
 		} else if ctx.Config().IsEnvTrue("AUTO_PATTERN_INITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=pattern")
+		} else if ctx.Config().IsEnvTrue("AUTO_UNINITIALIZE") {
+			flags = append(flags, "-ftrivial-auto-var-init=uninitialized")
 		}
 
 		return strings.Join(flags, " ")
diff --git a/cc/library.go b/cc/library.go
index 99a3e16..6c8f5bf 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1104,7 +1104,7 @@
 			if ctx.isVndkSp() {
 				library.baseInstaller.subDir = "vndk-sp"
 			} else if ctx.isVndk() {
-				if !ctx.mustUseVendorVariant() {
+				if !ctx.mustUseVendorVariant() && !ctx.isVndkExt() {
 					library.checkSameCoreVariant = true
 					if ctx.DeviceConfig().VndkUseCoreVariant() {
 						library.useCoreVariant = true
diff --git a/cc/linker.go b/cc/linker.go
index c2b4a3a..5a16cbd 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -164,6 +164,9 @@
 
 	// local file name to pass to the linker as --version_script
 	Version_script *string `android:"path,arch_variant"`
+
+	// list of static libs that should not be used to build this module
+	Exclude_static_libs []string
 }
 
 func NewBaseLinker(sanitize *sanitize) *baseLinker {
@@ -209,6 +212,8 @@
 	deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, linker.Properties.Export_shared_lib_headers...)
 	deps.ReexportGeneratedHeaders = append(deps.ReexportGeneratedHeaders, linker.Properties.Export_generated_headers...)
 
+	deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Exclude_static_libs)
+
 	if Bool(linker.Properties.Use_version_lib) {
 		deps.WholeStaticLibs = append(deps.WholeStaticLibs, "libbuildversion")
 	}
@@ -362,6 +367,8 @@
 				flags.Global.LdFlags = append(flags.Global.LdFlags,
 					"-Wl,--pack-dyn-relocs=android+relr",
 					"-Wl,--use-android-relr-tags")
+			} else if CheckSdkVersionAtLeast(ctx, 23) {
+				flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--pack-dyn-relocs=android")
 			}
 		}
 	} else {
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index a9be612..a95aca9 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -667,8 +667,10 @@
 		return nil
 	}
 	var err error
-	fiz.reader, err = zip.OpenReader(fiz.Name())
-	return err
+	if fiz.reader, err = zip.OpenReader(fiz.Name()); err != nil {
+		return fmt.Errorf("%s: %s", fiz.Name(), err.Error())
+	}
+	return nil
 }
 
 func main() {
diff --git a/java/aar.go b/java/aar.go
index 1ba65dc..d707e36 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -177,7 +177,10 @@
 	linkDeps = append(linkDeps, assetFiles...)
 
 	// SDK version flags
-	minSdkVersion := sdkVersionOrDefault(ctx, sdkContext.minSdkVersion())
+	minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersionString(ctx)
+	if err != nil {
+		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+	}
 
 	linkFlags = append(linkFlags, "--min-sdk-version "+minSdkVersion)
 	linkFlags = append(linkFlags, "--target-sdk-version "+minSdkVersion)
@@ -524,22 +527,22 @@
 	exportedStaticPackages android.Paths
 }
 
-func (a *AARImport) sdkVersion() string {
-	return String(a.properties.Sdk_version)
+func (a *AARImport) sdkVersion() sdkSpec {
+	return sdkSpecFrom(String(a.properties.Sdk_version))
 }
 
 func (a *AARImport) systemModules() string {
 	return ""
 }
 
-func (a *AARImport) minSdkVersion() string {
+func (a *AARImport) minSdkVersion() sdkSpec {
 	if a.properties.Min_sdk_version != nil {
-		return *a.properties.Min_sdk_version
+		return sdkSpecFrom(*a.properties.Min_sdk_version)
 	}
 	return a.sdkVersion()
 }
 
-func (a *AARImport) targetSdkVersion() string {
+func (a *AARImport) targetSdkVersion() sdkSpec {
 	return a.sdkVersion()
 }
 
diff --git a/java/android_manifest.go b/java/android_manifest.go
index dc7a3fc..e3646f5 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -59,7 +59,7 @@
 	if isLibrary {
 		args = append(args, "--library")
 	} else {
-		minSdkVersion, err := sdkVersionToNumber(ctx, sdkContext.minSdkVersion())
+		minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersion(ctx)
 		if err != nil {
 			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 		}
@@ -92,15 +92,28 @@
 	}
 
 	var deps android.Paths
-	targetSdkVersion := sdkVersionOrDefault(ctx, sdkContext.targetSdkVersion())
-	minSdkVersion := sdkVersionOrDefault(ctx, sdkContext.minSdkVersion())
-	if (UseApiFingerprint(ctx, sdkContext.targetSdkVersion()) ||
-		UseApiFingerprint(ctx, sdkContext.minSdkVersion())) {
-			apiFingerprint := ApiFingerprintPath(ctx)
-			deps = append(deps, apiFingerprint)
+	targetSdkVersion, err := sdkContext.targetSdkVersion().effectiveVersionString(ctx)
+	if err != nil {
+		ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
+	}
+	if UseApiFingerprint(ctx, targetSdkVersion) {
+		targetSdkVersion += fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+		deps = append(deps, ApiFingerprintPath(ctx))
+	}
+
+	minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersionString(ctx)
+	if err != nil {
+		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+	}
+	if UseApiFingerprint(ctx, minSdkVersion) {
+		minSdkVersion += fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+		deps = append(deps, ApiFingerprintPath(ctx))
 	}
 
 	fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
+	if err != nil {
+		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        manifestFixerRule,
 		Description: "fix manifest",
diff --git a/java/androidmk.go b/java/androidmk.go
index 7c19180..6d4d40b 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -93,7 +93,7 @@
 					if len(library.dexpreopter.builtInstalled) > 0 {
 						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", library.dexpreopter.builtInstalled)
 					}
-					entries.SetString("LOCAL_SDK_VERSION", library.sdkVersion())
+					entries.SetString("LOCAL_SDK_VERSION", library.sdkVersion().raw)
 					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar)
 					entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile)
 
@@ -174,7 +174,7 @@
 				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
 				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
-				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion())
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion().raw)
 				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
 			},
 		},
@@ -223,7 +223,7 @@
 				entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", prebuilt.proguardFlags)
 				entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", prebuilt.extraAaptPackagesFile)
 				entries.SetPath("LOCAL_FULL_MANIFEST_FILE", prebuilt.manifest)
-				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion())
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion().raw)
 			},
 		},
 	}}
@@ -663,11 +663,7 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(entries *android.AndroidMkEntries) {
 				entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", a.Privileged())
-				if a.certificate != nil {
-					entries.SetPath("LOCAL_CERTIFICATE", a.certificate.Pem)
-				} else {
-					entries.SetString("LOCAL_CERTIFICATE", "PRESIGNED")
-				}
+				entries.SetString("LOCAL_CERTIFICATE", a.certificate.AndroidMkString())
 				entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", a.properties.Overrides...)
 				if len(a.dexpreopter.builtInstalled) > 0 {
 					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", a.dexpreopter.builtInstalled)
diff --git a/java/app.go b/java/app.go
index c59047d..a27996c 100755
--- a/java/app.go
+++ b/java/app.go
@@ -154,16 +154,31 @@
 	return a.outputFile
 }
 
+func (a *AndroidApp) Certificate() Certificate {
+	return a.certificate
+}
+
 var _ AndroidLibraryDependency = (*AndroidApp)(nil)
 
 type Certificate struct {
-	Pem, Key android.Path
+	Pem, Key  android.Path
+	presigned bool
+}
+
+var presignedCertificate = Certificate{presigned: true}
+
+func (c Certificate) AndroidMkString() string {
+	if c.presigned {
+		return "PRESIGNED"
+	} else {
+		return c.Pem.String()
+	}
 }
 
 func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
 	a.Module.deps(ctx)
 
-	if String(a.appProperties.Stl) == "c++_shared" && a.sdkVersion() == "" {
+	if String(a.appProperties.Stl) == "c++_shared" && !a.sdkVersion().specified() {
 		ctx.PropertyErrorf("stl", "sdk_version must be set in order to use c++_shared")
 	}
 
@@ -212,7 +227,7 @@
 // Returns true if the native libraries should be stored in the APK uncompressed and the
 // extractNativeLibs application flag should be set to false in the manifest.
 func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool {
-	minSdkVersion, err := sdkVersionToNumber(ctx, a.minSdkVersion())
+	minSdkVersion, err := a.minSdkVersion().effectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.minSdkVersion(), err)
 	}
@@ -405,12 +420,15 @@
 		if certPropValue != "" {
 			defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
 			mainCert = Certificate{
-				defaultDir.Join(ctx, certPropValue+".x509.pem"),
-				defaultDir.Join(ctx, certPropValue+".pk8"),
+				Pem: defaultDir.Join(ctx, certPropValue+".x509.pem"),
+				Key: defaultDir.Join(ctx, certPropValue+".pk8"),
 			}
 		} else {
 			pem, key := ctx.Config().DefaultAppCertificate(ctx)
-			mainCert = Certificate{pem, key}
+			mainCert = Certificate{
+				Pem: pem,
+				Key: key,
+			}
 		}
 		certificates = append([]Certificate{mainCert}, certificates...)
 	}
@@ -798,8 +816,8 @@
 func (c *AndroidAppCertificate) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	cert := String(c.properties.Certificate)
 	c.Certificate = Certificate{
-		android.PathForModuleSrc(ctx, cert+".x509.pem"),
-		android.PathForModuleSrc(ctx, cert+".pk8"),
+		Pem: android.PathForModuleSrc(ctx, cert+".x509.pem"),
+		Key: android.PathForModuleSrc(ctx, cert+".pk8"),
 	}
 }
 
@@ -856,7 +874,7 @@
 	archVariants interface{}
 
 	outputFile  android.Path
-	certificate *Certificate
+	certificate Certificate
 
 	dexpreopter
 
@@ -1064,7 +1082,7 @@
 		if len(certificates) != 1 {
 			ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
 		}
-		a.certificate = &certificates[0]
+		a.certificate = certificates[0]
 		signed := android.PathForModuleOut(ctx, "signed", ctx.ModuleName()+".apk")
 		SignAppPackage(ctx, signed, dexOutput, certificates)
 		a.outputFile = signed
@@ -1072,6 +1090,7 @@
 		alignedApk := android.PathForModuleOut(ctx, "zip-aligned", ctx.ModuleName()+".apk")
 		TransformZipAlign(ctx, alignedApk, dexOutput)
 		a.outputFile = alignedApk
+		a.certificate = presignedCertificate
 	}
 
 	// TODO: Optionally compress the output apk.
@@ -1098,6 +1117,10 @@
 	return nil
 }
 
+func (a *AndroidAppImport) Certificate() Certificate {
+	return a.certificate
+}
+
 var dpiVariantGroupType reflect.Type
 var archVariantGroupType reflect.Type
 
@@ -1271,22 +1294,22 @@
 	ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
 }
 
-func (r *RuntimeResourceOverlay) sdkVersion() string {
-	return String(r.properties.Sdk_version)
+func (r *RuntimeResourceOverlay) sdkVersion() sdkSpec {
+	return sdkSpecFrom(String(r.properties.Sdk_version))
 }
 
 func (r *RuntimeResourceOverlay) systemModules() string {
 	return ""
 }
 
-func (r *RuntimeResourceOverlay) minSdkVersion() string {
+func (r *RuntimeResourceOverlay) minSdkVersion() sdkSpec {
 	if r.properties.Min_sdk_version != nil {
-		return *r.properties.Min_sdk_version
+		return sdkSpecFrom(*r.properties.Min_sdk_version)
 	}
 	return r.sdkVersion()
 }
 
-func (r *RuntimeResourceOverlay) targetSdkVersion() string {
+func (r *RuntimeResourceOverlay) targetSdkVersion() sdkSpec {
 	return r.sdkVersion()
 }
 
diff --git a/java/builder.go b/java/builder.go
index 26a49ea..f9b5367 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -38,7 +38,7 @@
 	// this, all java rules write into separate directories and then are combined into a .jar file
 	// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
 	// .srcjar files are unzipped into a temporary directory when compiled with javac.
-	javac = pctx.AndroidRemoteStaticRule("javac", android.SUPPORTS_GOMA,
+	javac = pctx.AndroidRemoteStaticRule("javac", android.RemoteRuleSupports{Goma: true, RBE: true, RBEFlag: android.RBE_JAVAC},
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
 				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
diff --git a/java/config/config.go b/java/config/config.go
index 6da7279..7f446e5 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -148,6 +148,20 @@
 		return ""
 	})
 
+	pctx.VariableFunc("R8Wrapper", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("R8_WRAPPER"); override != "" {
+			return override + " "
+		}
+		return ""
+	})
+
+	pctx.VariableFunc("D8Wrapper", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("D8_WRAPPER"); override != "" {
+			return override + " "
+		}
+		return ""
+	})
+
 	pctx.HostJavaToolVariable("JacocoCLIJar", "jacoco-cli.jar")
 
 	pctx.HostBinToolVariable("ManifestCheckCmd", "manifest_check")
diff --git a/java/dex.go b/java/dex.go
index cd6d90d..6afdb6d 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -22,10 +22,10 @@
 	"android/soong/android"
 )
 
-var d8 = pctx.AndroidStaticRule("d8",
+var d8 = pctx.AndroidRemoteStaticRule("d8", android.RemoteRuleSupports{RBE: true, RBEFlag: android.RBE_D8},
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-			`${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
+			`${config.D8Wrapper}${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
 			`${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
@@ -36,11 +36,11 @@
 	},
 	"outDir", "d8Flags", "zipFlags")
 
-var r8 = pctx.AndroidStaticRule("r8",
+var r8 = pctx.AndroidRemoteStaticRule("r8", android.RemoteRuleSupports{RBE: true, RBEFlag: android.RBE_R8},
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
 			`rm -f "$outDict" && ` +
-			`${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
+			`${config.R8Wrapper}${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
 			`--force-proguard-compatibility ` +
 			`--no-data-resources ` +
 			`-printmapping $outDict ` +
@@ -73,12 +73,12 @@
 			"--verbose")
 	}
 
-	minSdkVersion, err := sdkVersionToNumberAsString(ctx, j.minSdkVersion())
+	minSdkVersion, err := j.minSdkVersion().effectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err)
 	}
 
-	flags = append(flags, "--min-api "+minSdkVersion)
+	flags = append(flags, "--min-api "+minSdkVersion.asNumberString())
 	return flags
 }
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index c6aa7fe..87f6d5e 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -88,6 +88,9 @@
 	images     map[android.ArchType]android.OutputPath  // first image file
 	imagesDeps map[android.ArchType]android.OutputPaths // all files
 
+	// Only for extensions, paths to the primary boot images (grouped by target).
+	primaryImages map[android.ArchType]android.OutputPath
+
 	// File path to a zip archive with all image files (or nil, if not needed).
 	zip android.WritablePath
 }
@@ -355,7 +358,7 @@
 	}
 
 	if image.extension {
-		artImage := artBootImageConfig(ctx).images[arch]
+		artImage := image.primaryImages[arch]
 		cmd.
 			Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
 			Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":").
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 31bec93..637a32f 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -246,10 +246,12 @@
 
 		// specific to the framework config
 		frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
+		frameworkCfg.primaryImages = artCfg.images
 		frameworkCfg.imageLocations = append(artCfg.imageLocations, frameworkCfg.imageLocations...)
 
 		// specific to the jitzygote-framework config
 		frameworkJZCfg.dexPathsDeps = append(artJZCfg.dexPathsDeps, frameworkJZCfg.dexPathsDeps...)
+		frameworkJZCfg.primaryImages = artJZCfg.images
 		frameworkJZCfg.imageLocations = append(artJZCfg.imageLocations, frameworkJZCfg.imageLocations...)
 
 		return configs
diff --git a/java/droiddoc.go b/java/droiddoc.go
index a10ec81..abdceba 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -424,19 +424,19 @@
 
 var _ android.OutputFileProducer = (*Javadoc)(nil)
 
-func (j *Javadoc) sdkVersion() string {
-	return String(j.properties.Sdk_version)
+func (j *Javadoc) sdkVersion() sdkSpec {
+	return sdkSpecFrom(String(j.properties.Sdk_version))
 }
 
 func (j *Javadoc) systemModules() string {
 	return proptools.String(j.properties.System_modules)
 }
 
-func (j *Javadoc) minSdkVersion() string {
+func (j *Javadoc) minSdkVersion() sdkSpec {
 	return j.sdkVersion()
 }
 
-func (j *Javadoc) targetSdkVersion() string {
+func (j *Javadoc) targetSdkVersion() sdkSpec {
 	return j.sdkVersion()
 }
 
diff --git a/java/java.go b/java/java.go
index 4c6a5a5..ed3dca9 100644
--- a/java/java.go
+++ b/java/java.go
@@ -87,7 +87,7 @@
 	if j.SocSpecific() || j.DeviceSpecific() ||
 		(j.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
 		if sc, ok := ctx.Module().(sdkContext); ok {
-			if sc.sdkVersion() == "" {
+			if !sc.sdkVersion().specified() {
 				ctx.PropertyErrorf("sdk_version",
 					"sdk_version must have a value when the module is located at vendor or product(only if PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE is set).")
 			}
@@ -98,12 +98,11 @@
 func (j *Module) checkPlatformAPI(ctx android.ModuleContext) {
 	if sc, ok := ctx.Module().(sdkContext); ok {
 		usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis)
-		if usePlatformAPI != (sc.sdkVersion() == "") {
-			if usePlatformAPI {
-				ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
-			} else {
-				ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.")
-			}
+		sdkVersionSpecified := sc.sdkVersion().specified()
+		if usePlatformAPI && sdkVersionSpecified {
+			ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
+		} else if !usePlatformAPI && !sdkVersionSpecified {
+			ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.")
 		}
 
 	}
@@ -451,8 +450,8 @@
 }
 
 type SdkLibraryDependency interface {
-	SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths
-	SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths
+	SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths
+	SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths
 }
 
 type xref interface {
@@ -553,24 +552,24 @@
 			ctx.Config().UnbundledBuild())
 }
 
-func (j *Module) sdkVersion() string {
-	return String(j.deviceProperties.Sdk_version)
+func (j *Module) sdkVersion() sdkSpec {
+	return sdkSpecFrom(String(j.deviceProperties.Sdk_version))
 }
 
 func (j *Module) systemModules() string {
 	return proptools.String(j.deviceProperties.System_modules)
 }
 
-func (j *Module) minSdkVersion() string {
+func (j *Module) minSdkVersion() sdkSpec {
 	if j.deviceProperties.Min_sdk_version != nil {
-		return *j.deviceProperties.Min_sdk_version
+		return sdkSpecFrom(*j.deviceProperties.Min_sdk_version)
 	}
 	return j.sdkVersion()
 }
 
-func (j *Module) targetSdkVersion() string {
+func (j *Module) targetSdkVersion() sdkSpec {
 	if j.deviceProperties.Target_sdk_version != nil {
-		return *j.deviceProperties.Target_sdk_version
+		return sdkSpecFrom(*j.deviceProperties.Target_sdk_version)
 	}
 	return j.sdkVersion()
 }
@@ -776,26 +775,25 @@
 		name == "stub-annotations" || name == "private-stub-annotations-jar" ||
 		name == "core-lambda-stubs" || name == "core-generated-annotation-stubs":
 		return javaCore, true
-	case ver == "core_current":
+	case ver.kind == sdkCore:
 		return javaCore, false
 	case name == "android_system_stubs_current":
 		return javaSystem, true
-	case strings.HasPrefix(ver, "system_"):
+	case ver.kind == sdkSystem:
 		return javaSystem, false
 	case name == "android_test_stubs_current":
 		return javaSystem, true
-	case strings.HasPrefix(ver, "test_"):
+	case ver.kind == sdkTest:
 		return javaPlatform, false
 	case name == "android_stubs_current":
 		return javaSdk, true
-	case ver == "current":
+	case ver.kind == sdkPublic:
 		return javaSdk, false
-	case ver == "" || ver == "none" || ver == "core_platform":
+	case ver.kind == sdkPrivate || ver.kind == sdkNone || ver.kind == sdkCorePlatform:
 		return javaPlatform, false
+	case !ver.valid():
+		panic(fmt.Errorf("sdk_version is invalid. got %q", ver.raw))
 	default:
-		if _, err := strconv.Atoi(ver); err != nil {
-			panic(fmt.Errorf("expected sdk_version to be a number, got %q", ver))
-		}
 		return javaSdk, false
 	}
 }
@@ -992,19 +990,7 @@
 }
 
 func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext sdkContext) javaVersion {
-	v := sdkContext.sdkVersion()
-	// For PDK builds, use the latest SDK version instead of "current"
-	if ctx.Config().IsPdkBuild() &&
-		(v == "" || v == "none" || v == "core_platform" || v == "current") {
-		sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
-		latestSdkVersion := 0
-		if len(sdkVersions) > 0 {
-			latestSdkVersion = sdkVersions[len(sdkVersions)-1]
-		}
-		v = strconv.Itoa(latestSdkVersion)
-	}
-
-	sdk, err := sdkVersionToNumber(ctx, v)
+	sdk, err := sdkContext.sdkVersion().effectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("sdk_version", "%s", err)
 	}
@@ -1741,6 +1727,11 @@
 }
 
 func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bool {
+	// Store uncompressed (and aligned) any dex files from jars in APEXes.
+	if am, ok := ctx.Module().(android.ApexModule); ok && !am.IsForPlatform() {
+		return true
+	}
+
 	// Store uncompressed (and do not strip) dex files from boot class path jars.
 	if inList(ctx.ModuleName(), ctx.Config().BootJars()) {
 		return true
@@ -2282,11 +2273,11 @@
 	exportedSdkLibs       []string
 }
 
-func (j *Import) sdkVersion() string {
-	return String(j.properties.Sdk_version)
+func (j *Import) sdkVersion() sdkSpec {
+	return sdkSpecFrom(String(j.properties.Sdk_version))
 }
 
-func (j *Import) minSdkVersion() string {
+func (j *Import) minSdkVersion() sdkSpec {
 	return j.sdkVersion()
 }
 
diff --git a/java/kotlin.go b/java/kotlin.go
index 5319a4f..cb7da20 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -26,7 +26,7 @@
 	"github.com/google/blueprint"
 )
 
-var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.SUPPORTS_GOMA,
+var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.RemoteRuleSupports{Goma: true},
 	blueprint.RuleParams{
 		Command: `rm -rf "$classesDir" "$srcJarDir" "$kotlinBuildFile" "$emptyDir" && ` +
 			`mkdir -p "$classesDir" "$srcJarDir" "$emptyDir" && ` +
@@ -88,7 +88,7 @@
 	})
 }
 
-var kapt = pctx.AndroidRemoteStaticRule("kapt", android.SUPPORTS_GOMA,
+var kapt = pctx.AndroidRemoteStaticRule("kapt", android.RemoteRuleSupports{Goma: true},
 	blueprint.RuleParams{
 		Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && mkdir -p "$srcJarDir" "$kaptDir" && ` +
 			`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
diff --git a/java/sdk.go b/java/sdk.go
index 2dbcf4a..f388358 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -38,14 +38,16 @@
 var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey")
 
 type sdkContext interface {
-	// sdkVersion returns the sdk_version property of the current module, or an empty string if it is not set.
-	sdkVersion() string
+	// sdkVersion returns sdkSpec that corresponds to the sdk_version property of the current module
+	sdkVersion() sdkSpec
 	// systemModules returns the system_modules property of the current module, or an empty string if it is not set.
 	systemModules() string
-	// minSdkVersion returns the min_sdk_version property of the current module, or sdkVersion() if it is not set.
-	minSdkVersion() string
-	// targetSdkVersion returns the target_sdk_version property of the current module, or sdkVersion() if it is not set.
-	targetSdkVersion() string
+	// minSdkVersion returns sdkSpec that corresponds to the min_sdk_version property of the current module,
+	// or from sdk_version if it is not set.
+	minSdkVersion() sdkSpec
+	// targetSdkVersion returns the sdkSpec that corresponds to the target_sdk_version property of the current module,
+	// or from sdk_version if it is not set.
+	targetSdkVersion() sdkSpec
 }
 
 func UseApiFingerprint(ctx android.BaseModuleContext, v string) bool {
@@ -58,79 +60,236 @@
 	return false
 }
 
-func sdkVersionOrDefault(ctx android.BaseModuleContext, v string) string {
-	var sdkVersion string
-	switch v {
-	case "", "none", "current", "test_current", "system_current", "core_current", "core_platform":
-		sdkVersion = ctx.Config().DefaultAppTargetSdk()
+// sdkKind represents a particular category of an SDK spec like public, system, test, etc.
+type sdkKind int
+
+const (
+	sdkInvalid sdkKind = iota
+	sdkNone
+	sdkCore
+	sdkCorePlatform
+	sdkPublic
+	sdkSystem
+	sdkTest
+	sdkPrivate
+)
+
+// String returns the string representation of this sdkKind
+func (k sdkKind) String() string {
+	switch k {
+	case sdkPrivate:
+		return "private"
+	case sdkNone:
+		return "none"
+	case sdkPublic:
+		return "public"
+	case sdkSystem:
+		return "system"
+	case sdkTest:
+		return "test"
+	case sdkCore:
+		return "core"
+	case sdkCorePlatform:
+		return "core_platform"
 	default:
-		sdkVersion = v
+		return "invalid"
 	}
-	if UseApiFingerprint(ctx, sdkVersion) {
-		apiFingerprint := ApiFingerprintPath(ctx)
-		sdkVersion += fmt.Sprintf(".$$(cat %s)", apiFingerprint.String())
-	}
-	return sdkVersion
 }
 
-// Returns a sdk version as a number.  For modules targeting an unreleased SDK (meaning it does not yet have a number)
-// it returns android.FutureApiLevel (10000).
-func sdkVersionToNumber(ctx android.EarlyModuleContext, v string) (int, error) {
-	switch v {
-	case "", "none", "current", "test_current", "system_current", "core_current", "core_platform":
-		return ctx.Config().DefaultAppTargetSdkInt(), nil
-	default:
-		n := android.GetNumericSdkVersion(v)
-		if i, err := strconv.Atoi(n); err != nil {
-			return -1, fmt.Errorf("invalid sdk version %q", n)
-		} else {
-			return i, nil
+// sdkVersion represents a specific version number of an SDK spec of a particular kind
+type sdkVersion int
+
+const (
+	// special version number for a not-yet-frozen SDK
+	sdkVersionCurrent sdkVersion = sdkVersion(android.FutureApiLevel)
+	// special version number to be used for SDK specs where version number doesn't
+	// make sense, e.g. "none", "", etc.
+	sdkVersionNone sdkVersion = sdkVersion(0)
+)
+
+// isCurrent checks if the sdkVersion refers to the not-yet-published version of an sdkKind
+func (v sdkVersion) isCurrent() bool {
+	return v == sdkVersionCurrent
+}
+
+// isNumbered checks if the sdkVersion refers to the published (a.k.a numbered) version of an sdkKind
+func (v sdkVersion) isNumbered() bool {
+	return !v.isCurrent() && v != sdkVersionNone
+}
+
+// String returns the string representation of this sdkVersion.
+func (v sdkVersion) String() string {
+	if v.isCurrent() {
+		return "current"
+	} else if v.isNumbered() {
+		return strconv.Itoa(int(v))
+	}
+	return "(no version)"
+}
+
+// asNumberString directly converts the numeric value of this sdk version as a string.
+// When isNumbered() is true, this method is the same as String(). However, for sdkVersionCurrent
+// and sdkVersionNone, this returns 10000 and 0 while String() returns "current" and "(no version"),
+// respectively.
+func (v sdkVersion) asNumberString() string {
+	return strconv.Itoa(int(v))
+}
+
+// sdkSpec represents the kind and the version of an SDK for a module to build against
+type sdkSpec struct {
+	kind    sdkKind
+	version sdkVersion
+	raw     string
+}
+
+// valid checks if this sdkSpec is well-formed. Note however that true doesn't mean that the
+// specified SDK actually exists.
+func (s sdkSpec) valid() bool {
+	return s.kind != sdkInvalid
+}
+
+// specified checks if this sdkSpec is well-formed and is not "".
+func (s sdkSpec) specified() bool {
+	return s.valid() && s.kind != sdkPrivate
+}
+
+// prebuiltSdkAvailableForUnbundledBuilt tells whether this sdkSpec can have a prebuilt SDK
+// that can be used for unbundled builds.
+func (s sdkSpec) prebuiltSdkAvailableForUnbundledBuild() bool {
+	// "", "none", and "core_platform" are not available for unbundled build
+	// as we don't/can't have prebuilt stub for the versions
+	return s.kind != sdkPrivate && s.kind != sdkNone && s.kind != sdkCorePlatform
+}
+
+// forPdkBuild converts this sdkSpec into another sdkSpec that is for the PDK builds.
+func (s sdkSpec) forPdkBuild(ctx android.EarlyModuleContext) sdkSpec {
+	// For PDK builds, use the latest SDK version instead of "current" or ""
+	if s.kind == sdkPrivate || s.kind == sdkPublic {
+		kind := s.kind
+		if kind == sdkPrivate {
+			// We don't have prebuilt SDK for private APIs, so use the public SDK
+			// instead. This looks odd, but that's how it has been done.
+			// TODO(b/148271073): investigate the need for this.
+			kind = sdkPublic
 		}
+		version := sdkVersion(LatestSdkVersionInt(ctx))
+		return sdkSpec{kind, version, s.raw}
 	}
+	return s
 }
 
-func sdkVersionToNumberAsString(ctx android.EarlyModuleContext, v string) (string, error) {
-	n, err := sdkVersionToNumber(ctx, v)
-	if err != nil {
-		return "", err
+// usePrebuilt determines whether prebuilt SDK should be used for this sdkSpec with the given context.
+func (s sdkSpec) usePrebuilt(ctx android.EarlyModuleContext) bool {
+	if s.version.isCurrent() {
+		// "current" can be built from source and be from prebuilt SDK
+		return ctx.Config().UnbundledBuildUsePrebuiltSdks()
+	} else if s.version.isNumbered() {
+		// sanity check
+		if s.kind != sdkPublic && s.kind != sdkSystem && s.kind != sdkTest {
+			panic(fmt.Errorf("prebuilt SDK is not not available for sdkKind=%q", s.kind))
+			return false
+		}
+		// numbered SDKs are always from prebuilt
+		return true
 	}
-	return strconv.Itoa(n), nil
+	// "", "none", "core_platform" fall here
+	return false
+}
+
+// effectiveVersion converts an sdkSpec into the concrete sdkVersion that the module
+// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
+// it returns android.FutureApiLevel(10000).
+func (s sdkSpec) effectiveVersion(ctx android.EarlyModuleContext) (sdkVersion, error) {
+	if !s.valid() {
+		return s.version, fmt.Errorf("invalid sdk version %q", s.raw)
+	}
+	if ctx.Config().IsPdkBuild() {
+		s = s.forPdkBuild(ctx)
+	}
+	if s.version.isNumbered() {
+		return s.version, nil
+	}
+	return sdkVersion(ctx.Config().DefaultAppTargetSdkInt()), nil
+}
+
+// effectiveVersionString converts an sdkSpec into the concrete version string that the module
+// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
+// it returns the codename (P, Q, R, etc.)
+func (s sdkSpec) effectiveVersionString(ctx android.EarlyModuleContext) (string, error) {
+	ver, err := s.effectiveVersion(ctx)
+	if err == nil && int(ver) == ctx.Config().DefaultAppTargetSdkInt() {
+		return ctx.Config().DefaultAppTargetSdk(), nil
+	}
+	return ver.String(), err
+}
+
+func sdkSpecFrom(str string) sdkSpec {
+	switch str {
+	// special cases first
+	case "":
+		return sdkSpec{sdkPrivate, sdkVersionNone, str}
+	case "none":
+		return sdkSpec{sdkNone, sdkVersionNone, str}
+	case "core_platform":
+		return sdkSpec{sdkCorePlatform, sdkVersionNone, str}
+	default:
+		// the syntax is [kind_]version
+		sep := strings.LastIndex(str, "_")
+
+		var kindString string
+		if sep == 0 {
+			return sdkSpec{sdkInvalid, sdkVersionNone, str}
+		} else if sep == -1 {
+			kindString = ""
+		} else {
+			kindString = str[0:sep]
+		}
+		versionString := str[sep+1 : len(str)]
+
+		var kind sdkKind
+		switch kindString {
+		case "":
+			kind = sdkPublic
+		case "core":
+			kind = sdkCore
+		case "system":
+			kind = sdkSystem
+		case "test":
+			kind = sdkTest
+		default:
+			return sdkSpec{sdkInvalid, sdkVersionNone, str}
+		}
+
+		var version sdkVersion
+		if versionString == "current" {
+			version = sdkVersionCurrent
+		} else if i, err := strconv.Atoi(versionString); err == nil {
+			version = sdkVersion(i)
+		} else {
+			return sdkSpec{sdkInvalid, sdkVersionNone, str}
+		}
+
+		return sdkSpec{kind, version, str}
+	}
 }
 
 func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext sdkContext) sdkDep {
-	v := sdkContext.sdkVersion()
-
-	// For PDK builds, use the latest SDK version instead of "current"
-	if ctx.Config().IsPdkBuild() && (v == "" || v == "current") {
-		sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
-		latestSdkVersion := 0
-		if len(sdkVersions) > 0 {
-			latestSdkVersion = sdkVersions[len(sdkVersions)-1]
-		}
-		v = strconv.Itoa(latestSdkVersion)
-	}
-
-	numericSdkVersion, err := sdkVersionToNumber(ctx, v)
-	if err != nil {
-		ctx.PropertyErrorf("sdk_version", "%s", err)
+	sdkVersion := sdkContext.sdkVersion()
+	if !sdkVersion.valid() {
+		ctx.PropertyErrorf("sdk_version", "invalid version %q", sdkVersion.raw)
 		return sdkDep{}
 	}
 
-	toPrebuilt := func(sdk string) sdkDep {
-		var api, v string
-		if strings.Contains(sdk, "_") {
-			t := strings.Split(sdk, "_")
-			api = t[0]
-			v = t[1]
-		} else {
-			api = "public"
-			v = sdk
-		}
-		dir := filepath.Join("prebuilts", "sdk", v, api)
+	if ctx.Config().IsPdkBuild() {
+		sdkVersion = sdkVersion.forPdkBuild(ctx)
+	}
+
+	if sdkVersion.usePrebuilt(ctx) {
+		dir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), sdkVersion.kind.String())
 		jar := filepath.Join(dir, "android.jar")
 		// There's no aidl for other SDKs yet.
 		// TODO(77525052): Add aidl files for other SDKs too.
-		public_dir := filepath.Join("prebuilts", "sdk", v, "public")
+		public_dir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), "public")
 		aidl := filepath.Join(public_dir, "framework.aidl")
 		jarPath := android.ExistentPathForSource(ctx, jar)
 		aidlPath := android.ExistentPathForSource(ctx, aidl)
@@ -139,17 +298,17 @@
 		if (!jarPath.Valid() || !aidlPath.Valid()) && ctx.Config().AllowMissingDependencies() {
 			return sdkDep{
 				invalidVersion: true,
-				bootclasspath:  []string{fmt.Sprintf("sdk_%s_%s_android", api, v)},
+				bootclasspath:  []string{fmt.Sprintf("sdk_%s_%s_android", sdkVersion.kind, sdkVersion.version.String())},
 			}
 		}
 
 		if !jarPath.Valid() {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdk, jar)
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.raw, jar)
 			return sdkDep{}
 		}
 
 		if !aidlPath.Valid() {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdk, aidl)
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.raw, aidl)
 			return sdkDep{}
 		}
 
@@ -173,31 +332,26 @@
 
 	// Ensures that the specificed system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor apks)
 	// or PRODUCT_SYSTEMSDK_VERSIONS (for other apks or when BOARD_SYSTEMSDK_VERSIONS is not set)
-	if strings.HasPrefix(v, "system_") && numericSdkVersion != android.FutureApiLevel {
+	if sdkVersion.kind == sdkSystem && sdkVersion.version.isNumbered() {
 		allowed_versions := ctx.DeviceConfig().PlatformSystemSdkVersions()
 		if ctx.DeviceSpecific() || ctx.SocSpecific() {
 			if len(ctx.DeviceConfig().SystemSdkVersions()) > 0 {
 				allowed_versions = ctx.DeviceConfig().SystemSdkVersions()
 			}
 		}
-		if len(allowed_versions) > 0 && !android.InList(strconv.Itoa(numericSdkVersion), allowed_versions) {
+		if len(allowed_versions) > 0 && !android.InList(sdkVersion.version.String(), allowed_versions) {
 			ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
-				v, allowed_versions)
+				sdkVersion.raw, allowed_versions)
 		}
 	}
 
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
-		v != "" && v != "none" && v != "core_platform" {
-		return toPrebuilt(v)
-	}
-
-	switch v {
-	case "":
+	switch sdkVersion.kind {
+	case sdkPrivate:
 		return sdkDep{
 			useDefaultLibs:     true,
 			frameworkResModule: "framework-res",
 		}
-	case "none":
+	case sdkNone:
 		systemModules := sdkContext.systemModules()
 		if systemModules == "" {
 			ctx.PropertyErrorf("sdk_version",
@@ -214,22 +368,22 @@
 			systemModules:  systemModules,
 			bootclasspath:  []string{systemModules},
 		}
-	case "core_platform":
+	case sdkCorePlatform:
 		return sdkDep{
 			useDefaultLibs:     true,
 			frameworkResModule: "framework-res",
 			noFrameworksLibs:   true,
 		}
-	case "current":
+	case sdkPublic:
 		return toModule("android_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx))
-	case "system_current":
+	case sdkSystem:
 		return toModule("android_system_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx))
-	case "test_current":
+	case sdkTest:
 		return toModule("android_test_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx))
-	case "core_current":
+	case sdkCore:
 		return toModule("core.current.stubs", "", nil)
 	default:
-		return toPrebuilt(v)
+		panic(fmt.Errorf("invalid sdk %q", sdkVersion.raw))
 	}
 }
 
@@ -262,6 +416,15 @@
 	ctx.Config().Once(sdkVersionsKey, func() interface{} { return sdkVersions })
 }
 
+func LatestSdkVersionInt(ctx android.EarlyModuleContext) int {
+	sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
+	latestSdkVersion := 0
+	if len(sdkVersions) > 0 {
+		latestSdkVersion = sdkVersions[len(sdkVersions)-1]
+	}
+	return latestSdkVersion
+}
+
 func sdkSingletonFactory() android.Singleton {
 	return sdkSingleton{}
 }
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 72c4a7c..530e02c 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -650,27 +650,27 @@
 	mctx.CreateModule(android.PrebuiltEtcFactory, &etcProps)
 }
 
-func (module *SdkLibrary) PrebuiltJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
-	var api, v string
-	if sdkVersion == "" || sdkVersion == "none" {
-		api = "system"
-		v = "current"
-	} else if strings.Contains(sdkVersion, "_") {
-		t := strings.Split(sdkVersion, "_")
-		api = t[0]
-		v = t[1]
+func (module *SdkLibrary) PrebuiltJars(ctx android.BaseModuleContext, s sdkSpec) android.Paths {
+	var ver sdkVersion
+	var kind sdkKind
+	if s.usePrebuilt(ctx) {
+		ver = s.version
+		kind = s.kind
 	} else {
-		api = "public"
-		v = sdkVersion
+		// We don't have prebuilt SDK for the specific sdkVersion.
+		// Instead of breaking the build, fallback to use "system_current"
+		ver = sdkVersionCurrent
+		kind = sdkSystem
 	}
-	dir := filepath.Join("prebuilts", "sdk", v, api)
+
+	dir := filepath.Join("prebuilts", "sdk", ver.String(), kind.String())
 	jar := filepath.Join(dir, module.BaseModuleName()+".jar")
 	jarPath := android.ExistentPathForSource(ctx, jar)
 	if !jarPath.Valid() {
 		if ctx.Config().AllowMissingDependencies() {
 			return android.Paths{android.PathForSource(ctx, jar)}
 		} else {
-			ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", sdkVersion, jar)
+			ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", s.raw, jar)
 		}
 		return nil
 	}
@@ -678,32 +678,34 @@
 }
 
 // to satisfy SdkLibraryDependency interface
-func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
+func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
 	// This module is just a wrapper for the stubs.
 	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
 		return module.PrebuiltJars(ctx, sdkVersion)
 	} else {
-		if strings.HasPrefix(sdkVersion, "system_") {
+		switch sdkVersion.kind {
+		case sdkSystem:
 			return module.systemApiStubsPath
-		} else if sdkVersion == "" {
+		case sdkPrivate:
 			return module.Library.HeaderJars()
-		} else {
+		default:
 			return module.publicApiStubsPath
 		}
 	}
 }
 
 // to satisfy SdkLibraryDependency interface
-func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
+func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
 	// This module is just a wrapper for the stubs.
 	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
 		return module.PrebuiltJars(ctx, sdkVersion)
 	} else {
-		if strings.HasPrefix(sdkVersion, "system_") {
+		switch sdkVersion.kind {
+		case sdkSystem:
 			return module.systemApiStubsImplPath
-		} else if sdkVersion == "" {
+		case sdkPrivate:
 			return module.Library.ImplementationJars()
-		} else {
+		default:
 			return module.publicApiStubsImplPath
 		}
 	}
@@ -923,13 +925,13 @@
 }
 
 // to satisfy SdkLibraryDependency interface
-func (module *sdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
+func (module *sdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
 	// This module is just a wrapper for the prebuilt stubs.
 	return module.stubsPath
 }
 
 // to satisfy SdkLibraryDependency interface
-func (module *sdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
+func (module *sdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
 	// This module is just a wrapper for the stubs.
 	return module.stubsPath
 }
diff --git a/ui/build/config.go b/ui/build/config.go
index 9b19ede..5b9d10a 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -787,6 +787,48 @@
 	return false
 }
 
+func (c *configImpl) UseRBEJAVAC() bool {
+	if !c.UseRBE() {
+		return false
+	}
+
+	if v, ok := c.environ.Get("RBE_JAVAC"); ok {
+		v = strings.TrimSpace(v)
+		if v != "" && v != "false" {
+			return true
+		}
+	}
+	return false
+}
+
+func (c *configImpl) UseRBER8() bool {
+	if !c.UseRBE() {
+		return false
+	}
+
+	if v, ok := c.environ.Get("RBE_R8"); ok {
+		v = strings.TrimSpace(v)
+		if v != "" && v != "false" {
+			return true
+		}
+	}
+	return false
+}
+
+func (c *configImpl) UseRBED8() bool {
+	if !c.UseRBE() {
+		return false
+	}
+
+	if v, ok := c.environ.Get("RBE_D8"); ok {
+		v = strings.TrimSpace(v)
+		if v != "" && v != "false" {
+			return true
+		}
+	}
+	return false
+}
+
 func (c *configImpl) StartRBE() bool {
 	if !c.UseRBE() {
 		return false
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index c3da38b..ce8f968 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -191,6 +191,8 @@
 		"CC_WRAPPER",
 		"CXX_WRAPPER",
 		"JAVAC_WRAPPER",
+		"R8_WRAPPER",
+		"D8_WRAPPER",
 
 		// ccache settings
 		"CCACHE_COMPILERCHECK",
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 0b56b67..22ec1f1 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -133,6 +133,8 @@
 			"FLAG_invocation_id",
 			"FLAG_log_dir",
 			"FLAG_platform",
+			"FLAG_remote_accept_cache",
+			"FLAG_remote_update_cache",
 			"FLAG_server_address",
 
 			// ccache settings