Merge "Remove __future__ references from python scripts" into main
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index d9a862c..9a9e568 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -15,12 +15,12 @@
 package aconfig
 
 import (
+	"android/soong/android"
 	"path/filepath"
 	"slices"
+	"strconv"
 	"strings"
 
-	"android/soong/android"
-
 	"github.com/google/blueprint"
 )
 
@@ -185,6 +185,13 @@
 				defaultPermission = confPerm
 			}
 		}
+		var allowReadWrite bool
+		if requireAllReadOnly, ok := ctx.Config().GetBuildFlag("RELEASE_ACONFIG_REQUIRE_ALL_READ_ONLY"); ok {
+			// The build flag (RELEASE_ACONFIG_REQUIRE_ALL_READ_ONLY) is the negation of the aconfig flag
+			// (allow-read-write) for historical reasons.
+			// Bool build flags are always "" for false, and generally "true" for true.
+			allowReadWrite = requireAllReadOnly == ""
+		}
 		inputFiles := make([]android.Path, len(declarationFiles))
 		copy(inputFiles, declarationFiles)
 		inputFiles = append(inputFiles, valuesFiles[config]...)
@@ -194,6 +201,7 @@
 			"declarations":       android.JoinPathsWithPrefix(declarationFiles, "--declarations "),
 			"values":             joinAndPrefix(" --values ", values[config]),
 			"default-permission": optionalVariable(" --default-permission ", defaultPermission),
+			"allow-read-write":   optionalVariable(" --allow-read-write ", strconv.FormatBool(allowReadWrite)),
 		}
 		if len(module.properties.Container) > 0 {
 			args["container"] = "--container " + module.properties.Container
diff --git a/aconfig/init.go b/aconfig/init.go
index 6f91d8e..621d619 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -32,6 +32,7 @@
 				` ${declarations}` +
 				` ${values}` +
 				` ${default-permission}` +
+				` ${allow-read-write}` +
 				` --cache ${out}.tmp` +
 				` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
 			//				` --build-id ${release_version}` +
@@ -39,7 +40,7 @@
 				"${aconfig}",
 			},
 			Restat: true,
-		}, "release_version", "package", "container", "declarations", "values", "default-permission")
+		}, "release_version", "package", "container", "declarations", "values", "default-permission", "allow-read-write")
 
 	// For create-device-config-sysprops: Generate aconfig flag value map text file
 	aconfigTextRule = pctx.AndroidStaticRule("aconfig_text",
diff --git a/android/hooks.go b/android/hooks.go
index 9f4e5b6..f8022d0 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -59,6 +59,16 @@
 	})
 }
 
+func AddLoadHookWithPriority(m blueprint.Module, hook func(LoadHookContext), priority int) {
+	blueprint.AddLoadHookWithPriority(m, func(ctx blueprint.LoadHookContext) {
+		actx := &loadHookContext{
+			earlyModuleContext: m.(Module).base().earlyModuleContextFactory(ctx),
+			bp:                 ctx,
+		}
+		hook(actx)
+	}, priority)
+}
+
 type loadHookContext struct {
 	earlyModuleContext
 	bp     blueprint.LoadHookContext
diff --git a/android/module.go b/android/module.go
index 7dda9ab..d703c19 100644
--- a/android/module.go
+++ b/android/module.go
@@ -338,6 +338,7 @@
 		}
 		Android struct {
 			Compile_multilib *string
+			Enabled          *bool
 		}
 	}
 
diff --git a/android/neverallow.go b/android/neverallow.go
index 566d73c..fdcbe1c 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -249,6 +249,7 @@
 			NotModuleType("prebuilt_sbin").
 			NotModuleType("prebuilt_system").
 			NotModuleType("prebuilt_first_stage_ramdisk").
+			NotModuleType("prebuilt_res").
 			Because("install_in_root is only for init_first_stage or librecovery_ui_ext."),
 	}
 }
@@ -344,7 +345,6 @@
 			"prebuilt_priv_app",
 			"prebuilt_rfs",
 			"prebuilt_framework",
-			"prebuilt_res",
 			"prebuilt_wlc_upt",
 			"prebuilt_odm",
 			"prebuilt_vendor_dlkm",
diff --git a/android/variable.go b/android/variable.go
index e06fb8a..baa2646 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -669,6 +669,8 @@
 	VendorRamdiskKernelOptionsFile   string   `json:",omitempty"`
 
 	ProductFsverityGenerateMetadata bool `json:",omitempty"`
+
+	TargetScreenDensity string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/apex.go b/apex/apex.go
index 1a598e5..f934134 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1850,7 +1850,7 @@
 // as the apex.
 func (a *apexBundle) enforcePartitionTagOnApexSystemServerJar(ctx android.ModuleContext) {
 	global := dexpreopt.GetGlobalConfig(ctx)
-	ctx.VisitDirectDepsWithTag(sscpfTag, func(child android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(sscpfTag, func(child android.ModuleProxy) {
 		info, ok := android.OtherModuleProvider(ctx, child, java.LibraryNameToPartitionInfoProvider)
 		if !ok {
 			ctx.ModuleErrorf("Could not find partition info of apex system server jars.")
@@ -2309,7 +2309,7 @@
 // Apexes built from source retrieve this information by visiting `bootclasspath_fragments`
 // Used by dex_bootjars to generate the boot image
 func (a *apexBundle) provideApexExportsInfo(ctx android.ModuleContext) {
-	ctx.VisitDirectDepsWithTag(bcpfTag, func(child android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(bcpfTag, func(child android.ModuleProxy) {
 		if info, ok := android.OtherModuleProvider(ctx, child, java.BootclasspathFragmentApexContentInfoProvider); ok {
 			exports := android.ApexExportsInfo{
 				ApexName:                      a.ApexVariationName(),
@@ -2340,7 +2340,7 @@
 	}
 	if a.Updatable() {
 		// checking direct deps is sufficient since apex->apk is a direct edge, even when inherited via apex_defaults
-		mctx.VisitDirectDeps(func(module android.Module) {
+		mctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 			if appInfo, ok := android.OtherModuleProvider(mctx, module, java.AppInfoProvider); ok {
 				// ignore android_test_app
 				if !appInfo.TestHelperApp && !appInfo.Updatable {
@@ -2885,7 +2885,7 @@
 	}
 
 	var appEmbeddedJNILibs android.Paths
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(dep)
 		if !checkApexTag(tag) {
 			return
diff --git a/fsgen/Android.bp b/fsgen/Android.bp
index a022581..e8065f3 100644
--- a/fsgen/Android.bp
+++ b/fsgen/Android.bp
@@ -17,6 +17,7 @@
         "filesystem_creator.go",
         "fsgen_mutators.go",
         "prebuilt_etc_modules_gen.go",
+        "util.go",
         "vbmeta_partitions.go",
     ],
     testSrcs: [
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index cd29dfd..745aeaa 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -415,6 +415,22 @@
 				"first_stage_ramdisk/sys",
 			})
 		}
+	case "recovery":
+		// Following https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2826;drc=ad7cfb56010cb22c3aa0e70cf71c804352553526
+		fsProps.Dirs = android.NewSimpleConfigurable([]string{
+			"sdcard",
+			"tmp",
+		})
+		fsProps.Symlinks = []filesystem.SymlinkDefinition{
+			{
+				Target: proptools.StringPtr("/system/bin/init"),
+				Name:   proptools.StringPtr("init"),
+			},
+			{
+				Target: proptools.StringPtr("prop.default"),
+				Name:   proptools.StringPtr("default.prop"),
+			},
+		}
 	}
 }
 
diff --git a/fsgen/fsgen_mutators.go b/fsgen/fsgen_mutators.go
index b99e2da..f0a54db 100644
--- a/fsgen/fsgen_mutators.go
+++ b/fsgen/fsgen_mutators.go
@@ -162,6 +162,9 @@
 			(*fsGenState.fsDeps["product"])["system_other_avbpubkey"] = defaultDepCandidateProps(ctx.Config())
 		}
 
+		// Add common resources `prebuilt_res` module as dep of recovery partition
+		(*fsGenState.fsDeps["recovery"])[fmt.Sprintf("recovery-resources-common-%s", getDpi(ctx))] = defaultDepCandidateProps(ctx.Config())
+
 		return &fsGenState
 	}).(*FsGenState)
 }
diff --git a/fsgen/util.go b/fsgen/util.go
new file mode 100644
index 0000000..9ab3ad8
--- /dev/null
+++ b/fsgen/util.go
@@ -0,0 +1,60 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsgen
+
+import (
+	"android/soong/android"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// Returns the appropriate dpi for recovery common resources selection. Replicates the logic in
+// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2536;drc=a6af369e71ded123734523ea640b97b70a557cb9
+func getDpi(ctx android.LoadHookContext) string {
+	recoveryDensity := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.TargetScreenDensity
+	if len(recoveryDensity) == 0 {
+		aaptPreferredConfig := ctx.Config().ProductAAPTPreferredConfig()
+		if len(aaptPreferredConfig) > 0 {
+			recoveryDensity = aaptPreferredConfig
+		} else {
+			recoveryDensity = "mdpi"
+		}
+	}
+	if !android.InList(recoveryDensity, []string{"xxxhdpi", "xxhdpi", "xhdpi", "hdpi", "mdpi"}) {
+		recoveryDensity = strings.TrimSuffix(recoveryDensity, "dpi")
+		dpiInt, err := strconv.ParseInt(recoveryDensity, 10, 64)
+		if err != nil {
+			panic(fmt.Sprintf("Error in parsing recoveryDensity: %s", err.Error()))
+		}
+		if dpiInt >= 560 {
+			recoveryDensity = "xxxhdpi"
+		} else if dpiInt >= 400 {
+			recoveryDensity = "xxhdpi"
+		} else if dpiInt >= 280 {
+			recoveryDensity = "xhdpi"
+		} else if dpiInt >= 200 {
+			recoveryDensity = "hdpi"
+		} else {
+			recoveryDensity = "mdpi"
+		}
+	}
+
+	if p := android.ExistentPathForSource(ctx, fmt.Sprintf("bootable/recovery/res-%s", recoveryDensity)); !p.Valid() {
+		recoveryDensity = "xhdpi"
+	}
+
+	return recoveryDensity
+}
diff --git a/java/aar.go b/java/aar.go
index e0e642e..1e5c95a 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -588,14 +588,16 @@
 		}
 	}
 
-	for _, dir := range overlayDirs {
-		compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files,
-			compileFlags, a.filterProduct(), opts.aconfigTextFiles).Paths()...)
-	}
-
 	var compiledRro, compiledRroOverlay android.Paths
 	if opts.rroDirs != nil {
 		compiledRro, compiledRroOverlay = a.compileResInDir(ctx, *opts.rroDirs, compileFlags, opts.aconfigTextFiles)
+	} else {
+		// RRO enforcement is done based on module name. Compile the overlayDirs only if rroDirs is nil.
+		// This ensures that the autogenerated RROs do not compile the overlay dirs twice.
+		for _, dir := range overlayDirs {
+			compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files,
+				compileFlags, a.filterProduct(), opts.aconfigTextFiles).Paths()...)
+		}
 	}
 
 	var splitPackages android.WritablePaths
diff --git a/java/androidmk.go b/java/androidmk.go
index 2ad30b1..b6bab53 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -307,15 +307,11 @@
 			Disabled: true,
 		}}
 	}
-	var required []string
-	if proptools.Bool(app.appProperties.Generate_product_characteristics_rro) {
-		required = []string{app.productCharacteristicsRROPackageName()}
-	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "APPS",
 		OutputFile: android.OptionalPathForPath(app.outputFile),
 		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
-		Required:   required,
+		Required:   app.requiredModuleNames,
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				// App module names can be overridden.
@@ -350,31 +346,6 @@
 					entries.SetBoolIfTrue("LOCAL_NO_STANDARD_LIBRARIES", true)
 				}
 
-				filterRRO := func(filter overlayType) android.Paths {
-					var paths android.Paths
-					seen := make(map[android.Path]bool)
-					for _, d := range app.rroDirsDepSet.ToList() {
-						if d.overlayType == filter {
-							if seen[d.path] {
-								continue
-							}
-							seen[d.path] = true
-							paths = append(paths, d.path)
-						}
-					}
-					// Reverse the order, Soong stores rroDirs in aapt2 order (low to high priority), but Make
-					// expects it in LOCAL_RESOURCE_DIRS order (high to low priority).
-					return android.ReversePaths(paths)
-				}
-				deviceRRODirs := filterRRO(device)
-				if len(deviceRRODirs) > 0 {
-					entries.AddStrings("LOCAL_SOONG_DEVICE_RRO_DIRS", deviceRRODirs.Strings()...)
-				}
-				productRRODirs := filterRRO(product)
-				if len(productRRODirs) > 0 {
-					entries.AddStrings("LOCAL_SOONG_PRODUCT_RRO_DIRS", productRRODirs.Strings()...)
-				}
-
 				entries.SetBoolIfTrue("LOCAL_EXPORT_PACKAGE_RESOURCES", Bool(app.appProperties.Export_package_resources))
 
 				entries.SetPath("LOCAL_FULL_MANIFEST_FILE", app.manifestPath)
@@ -437,7 +408,7 @@
 		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_CERTIFICATE", "presigned") // The apk will be signed by soong
+				entries.SetString("LOCAL_CERTIFICATE", "PRESIGNED") // The apk will be signed by soong
 			},
 		},
 	}}
diff --git a/java/app.go b/java/app.go
index 4dea7dc..bedb45c 100644
--- a/java/app.go
+++ b/java/app.go
@@ -223,6 +223,8 @@
 	javaApiUsedByOutputFile android.ModuleOutPath
 
 	privAppAllowlist android.OptionalPath
+
+	requiredModuleNames []string
 }
 
 func (a *AndroidApp) IsInstallable() bool {
@@ -421,6 +423,24 @@
 		TestHelperApp:   false,
 		EmbeddedJNILibs: embeddedJniLibs,
 	})
+
+	a.requiredModuleNames = a.getRequiredModuleNames(ctx)
+}
+
+func (a *AndroidApp) getRequiredModuleNames(ctx android.ModuleContext) []string {
+	var required []string
+	if proptools.Bool(a.appProperties.Generate_product_characteristics_rro) {
+		required = []string{a.productCharacteristicsRROPackageName()}
+	}
+	// Install the vendor overlay variant if this app is installed.
+	if len(filterRRO(a.rroDirsDepSet, device)) > 0 {
+		required = append(required, AutogeneratedRroModuleName(ctx, ctx.Module().Name(), "vendor"))
+	}
+	// Install the product overlay variant if this app is installed.
+	if len(filterRRO(a.rroDirsDepSet, product)) > 0 {
+		required = append(required, AutogeneratedRroModuleName(ctx, ctx.Module().Name(), "product"))
+	}
+	return required
 }
 
 func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
@@ -1388,6 +1408,11 @@
 			}
 		}
 		ctx.CreateModule(RuntimeResourceOverlayFactory, &rroProperties)
+
+	})
+
+	module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
+		createInternalRuntimeOverlays(ctx, module.ModuleBase)
 	})
 
 	return module
@@ -1397,6 +1422,68 @@
 	return fmt.Sprintf("%s__%s__auto_generated_rro_%s", moduleName, ctx.Config().DeviceProduct(), partition)
 }
 
+type createModuleContext interface {
+	android.EarlyModuleContext
+	CreateModule(android.ModuleFactory, ...interface{}) android.Module
+}
+
+func createInternalRuntimeOverlays(ctx createModuleContext, a android.ModuleBase) {
+	if !ctx.Config().HasDeviceProduct() {
+		return
+	}
+	// vendor
+	vendorOverlayProps := struct {
+		Name                *string
+		Base                *string
+		Vendor              *bool
+		Product_specific    *bool
+		System_ext_specific *bool
+		Manifest            *string
+		Sdk_version         *string
+		Compile_multilib    *string
+		Enabled             proptools.Configurable[bool]
+	}{
+		Name:                proptools.StringPtr(AutogeneratedRroModuleName(ctx, a.Name(), "vendor")),
+		Base:                proptools.StringPtr(a.Name()),
+		Vendor:              proptools.BoolPtr(true),
+		Product_specific:    proptools.BoolPtr(false),
+		System_ext_specific: proptools.BoolPtr(false),
+		Manifest:            proptools.StringPtr(":" + a.Name() + "{.manifest.xml}"),
+		Sdk_version:         proptools.StringPtr("current"),
+		Compile_multilib:    proptools.StringPtr("first"),
+		Enabled:             a.EnabledProperty().Clone(),
+	}
+	ctx.CreateModule(AutogenRuntimeResourceOverlayFactory, &vendorOverlayProps)
+
+	// product
+	productOverlayProps := struct {
+		Name                *string
+		Base                *string
+		Vendor              *bool
+		Proprietary         *bool
+		Soc_specific        *bool
+		Product_specific    *bool
+		System_ext_specific *bool
+		Manifest            *string
+		Sdk_version         *string
+		Compile_multilib    *string
+		Enabled             proptools.Configurable[bool]
+	}{
+		Name:                proptools.StringPtr(AutogeneratedRroModuleName(ctx, a.Name(), "product")),
+		Base:                proptools.StringPtr(a.Name()),
+		Vendor:              proptools.BoolPtr(false),
+		Proprietary:         proptools.BoolPtr(false),
+		Soc_specific:        proptools.BoolPtr(false),
+		Product_specific:    proptools.BoolPtr(true),
+		System_ext_specific: proptools.BoolPtr(false),
+		Manifest:            proptools.StringPtr(":" + a.Name() + "{.manifest.xml}"),
+		Sdk_version:         proptools.StringPtr("current"),
+		Compile_multilib:    proptools.StringPtr("first"),
+		Enabled:             a.EnabledProperty().Clone(),
+	}
+	ctx.CreateModule(AutogenRuntimeResourceOverlayFactory, &productOverlayProps)
+}
+
 // A dictionary of values to be overridden in the manifest.
 type Manifest_values struct {
 	// Overrides the value of package_name in the manifest
@@ -1707,6 +1794,10 @@
 
 	android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
 	android.InitOverrideModule(m)
+	android.AddLoadHookWithPriority(m, func(ctx android.LoadHookContext) {
+		createInternalRuntimeOverlays(ctx, m.ModuleBase)
+	}, 1) // Run after soong config load hoook
+
 	return m
 }
 
diff --git a/java/app_test.go b/java/app_test.go
index 3d83ea1..11556b0 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -4727,3 +4727,147 @@
 		"out/soong/.intermediates/foo/android_common/aapt2/res/values_strings.(test.package.flag1).arsc.flat",
 	)
 }
+
+func TestAutogeneratedStaticRro(t *testing.T) {
+	t.Parallel()
+	bp := `
+android_app {
+	name: "foo",
+	srcs: ["foo.java"],
+	platform_apis: true,
+}
+override_android_app {
+	name: "override_foo",
+	base: "foo",
+}
+`
+	testCases := []struct {
+		desc               string
+		preparer           android.FixturePreparer
+		overlayApkExpected bool
+	}{
+		{
+			desc:               "No DEVICE_PACKAGE_OVERLAYS, no overlay .apk file",
+			overlayApkExpected: false,
+		},
+		{
+			desc:               "DEVICE_PACKAGE_OVERLAYS exists, but the directory is empty",
+			overlayApkExpected: false,
+			preparer: android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.DeviceResourceOverlays = []string{"device/company/test_product"}
+			}),
+		},
+		{
+			desc:               "DEVICE_PACKAGE_OVERLAYS exists, directory is non-empty, but does not contain a matching resource dir",
+			overlayApkExpected: false,
+			preparer: android.GroupFixturePreparers(
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.DeviceResourceOverlays = []string{"device/company/test_product"}
+				}),
+				android.MockFS{
+					"res/foo.xml": nil,
+					"device/company/test_product/different_res/foo.xml": nil, // different dir
+				}.AddToFixture(),
+			),
+		},
+		{
+			desc:               "DEVICE_PACKAGE_OVERLAYS and the directory contain a matching resource dir",
+			overlayApkExpected: true,
+			preparer: android.GroupFixturePreparers(
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.DeviceResourceOverlays = []string{"device/company/test_product"}
+				}),
+				android.MockFS{
+					"res/foo.xml": nil,
+					"device/company/test_product/res/foo.xml": nil,
+				}.AddToFixture(),
+			),
+		},
+	}
+	for _, tc := range testCases {
+		result := android.GroupFixturePreparers(
+			PrepareForTestWithJavaDefaultModules,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.EnforceRROTargets = []string{"*"}
+			}),
+			android.OptionalFixturePreparer(tc.preparer),
+		).RunTestWithBp(t, bp)
+		vendorOverlayApk := result.ModuleForTests("foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("foo__test_product__auto_generated_rro_vendor.apk")
+		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, vendorOverlayApk.Rule != nil)
+		overrideVendorOverlayApk := result.ModuleForTests("override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("override_foo__test_product__auto_generated_rro_vendor.apk")
+		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.Rule != nil)
+	}
+}
+
+func TestNoAutogeneratedStaticRroForDisabledOverrideApps(t *testing.T) {
+	t.Parallel()
+	bp := `
+soong_config_module_type {
+	name: "my_custom_override_android_app",
+	module_type: "override_android_app",
+	config_namespace: "my_namespace",
+	value_variables: ["my_app_enabled"],
+	properties: ["enabled"],
+}
+soong_config_bool_variable {
+	name: "my_app_enabled",
+}
+android_app {
+	name: "foo",
+	srcs: ["foo.java"],
+	platform_apis: true,
+}
+my_custom_override_android_app {
+	name: "override_foo",
+	base: "foo",
+	soong_config_variables: {
+		my_app_enabled: {
+			enabled: true,
+			conditions_default: {
+				enabled: false
+			},
+		},
+	}
+}
+`
+	testCases := []struct {
+		desc               string
+		preparer           android.FixturePreparer
+		overlayApkExpected bool
+	}{
+		{
+			desc:               "my_app_enabled is empty",
+			overlayApkExpected: false,
+		},
+		{
+			desc:               "my_app_enabled is true",
+			overlayApkExpected: true,
+			preparer: android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.VendorVars = map[string]map[string]string{
+					"my_namespace": {
+						"my_app_enabled": "true",
+					},
+				}
+			}),
+		},
+	}
+	for _, tc := range testCases {
+		result := android.GroupFixturePreparers(
+			PrepareForTestWithJavaDefaultModules,
+			android.PrepareForTestWithSoongConfigModuleBuildComponents,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.EnforceRROTargets = []string{"*"}
+			}),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.DeviceResourceOverlays = []string{"device/company/test_product"}
+			}),
+			android.MockFS{
+				"res/foo.xml": nil,
+				"device/company/test_product/res/foo.xml": nil,
+			}.AddToFixture(),
+			android.OptionalFixturePreparer(tc.preparer),
+		).RunTestWithBp(t, bp)
+		overrideVendorOverlayApk := result.ModuleForTests("override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").Module().(*AutogenRuntimeResourceOverlay)
+		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.exportPackage != nil)
+	}
+}
diff --git a/java/rro_test.go b/java/rro_test.go
index 4d79130..4d58bb4 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -255,103 +255,6 @@
 	}
 }
 
-func TestEnforceRRO_propagatesToDependencies(t *testing.T) {
-	testCases := []struct {
-		name              string
-		enforceRROTargets []string
-		rroDirs           map[string][]string
-	}{
-		{
-			name:              "no RRO",
-			enforceRROTargets: nil,
-			rroDirs: map[string][]string{
-				"foo": nil,
-				"bar": nil,
-			},
-		},
-		{
-			name:              "enforce RRO on all",
-			enforceRROTargets: []string{"*"},
-			rroDirs: map[string][]string{
-				"foo": {"product/vendor/blah/overlay/lib2/res"},
-				"bar": {"product/vendor/blah/overlay/lib2/res"},
-			},
-		},
-		{
-			name:              "enforce RRO on foo",
-			enforceRROTargets: []string{"foo"},
-			rroDirs: map[string][]string{
-				"foo": {"product/vendor/blah/overlay/lib2/res"},
-				"bar": nil,
-			},
-		},
-	}
-
-	productResourceOverlays := []string{
-		"product/vendor/blah/overlay",
-	}
-
-	fs := android.MockFS{
-		"lib2/res/values/strings.xml":                             nil,
-		"product/vendor/blah/overlay/lib2/res/values/strings.xml": nil,
-	}
-
-	bp := `
-			android_app {
-				name: "foo",
-				sdk_version: "current",
-				resource_dirs: [],
-				static_libs: ["lib"],
-			}
-
-			android_app {
-				name: "bar",
-				sdk_version: "current",
-				resource_dirs: [],
-				static_libs: ["lib"],
-			}
-
-			android_library {
-				name: "lib",
-				sdk_version: "current",
-				resource_dirs: [],
-				static_libs: ["lib2"],
-			}
-
-			android_library {
-				name: "lib2",
-				sdk_version: "current",
-				resource_dirs: ["lib2/res"],
-			}
-		`
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-			result := android.GroupFixturePreparers(
-				PrepareForTestWithJavaDefaultModules,
-				fs.AddToFixture(),
-				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-					variables.ProductResourceOverlays = productResourceOverlays
-					if testCase.enforceRROTargets != nil {
-						variables.EnforceRROTargets = testCase.enforceRROTargets
-					}
-				}),
-			).RunTestWithBp(t, bp)
-
-			modules := []string{"foo", "bar"}
-			for _, moduleName := range modules {
-				module := result.ModuleForTests(moduleName, "android_common")
-				mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0]
-				actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
-				if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
-					t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
-						moduleName, testCase.rroDirs[moduleName], actualRRODirs)
-				}
-			}
-		})
-	}
-}
-
 func TestRuntimeResourceOverlayPartition(t *testing.T) {
 	bp := `
 		runtime_resource_overlay {