diff --git a/android/config.go b/android/config.go index 8df65f720..2675402eb 100644 --- a/android/config.go +++ b/android/config.go @@ -916,6 +916,10 @@ func (c *config) EnforceRROForModule(name string) bool { return false } +func (c *config) EnforceRROExemptedForModule(name string) bool { + return InList(name, c.productVariables.EnforceRROExemptedTargets) +} + func (c *config) EnforceRROExcludedOverlay(path string) bool { excluded := c.productVariables.EnforceRROExcludedOverlays if len(excluded) > 0 { diff --git a/java/aar.go b/java/aar.go index 9cab0bdca..c31fc9530 100644 --- a/java/aar.go +++ b/java/aar.go @@ -34,10 +34,16 @@ type AndroidLibraryDependency interface { ExportedStaticPackages() android.Paths ExportedManifests() android.Paths ExportedAssets() android.OptionalPath + SetRROEnforcedForDependent(enforce bool) + IsRROEnforced(ctx android.BaseModuleContext) bool } func init() { RegisterAARBuildComponents(android.InitRegistrationContext) + + android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel() + }) } func RegisterAARBuildComponents(ctx android.RegistrationContext) { @@ -82,6 +88,9 @@ type aaptProperties struct { // do not include AndroidManifest from dependent libraries Dont_merge_manifests *bool + + // true if RRO is enforced for any of the dependent modules + RROEnforcedForDependent bool `blueprint:"mutated"` } type aapt struct { @@ -117,6 +126,18 @@ type split struct { path android.Path } +// Propagate RRO enforcement flag to static lib dependencies transitively. +func propagateRROEnforcementMutator(ctx android.TopDownMutatorContext) { + m := ctx.Module() + if d, ok := m.(AndroidLibraryDependency); ok && d.IsRROEnforced(ctx) { + ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) { + if a, ok := d.(AndroidLibraryDependency); ok { + a.SetRROEnforcedForDependent(true) + } + }) + } +} + func (a *aapt) ExportPackage() android.Path { return a.exportPackage } @@ -133,6 +154,17 @@ func (a *aapt) ExportedAssets() android.OptionalPath { return a.assetPackage } +func (a *aapt) SetRROEnforcedForDependent(enforce bool) { + a.aaptProperties.RROEnforcedForDependent = enforce +} + +func (a *aapt) IsRROEnforced(ctx android.BaseModuleContext) bool { + // True if RRO is enforced for this module or... + return ctx.Config().EnforceRROForModule(ctx.ModuleName()) || + // if RRO is enforced for any of its dependents, and this module is not exempted. + (a.aaptProperties.RROEnforcedForDependent && !ctx.Config().EnforceRROExemptedForModule(ctx.ModuleName())) +} + func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext, manifestPath android.Path) (compileFlags, linkFlags []string, linkDeps android.Paths, resDirs, overlayDirs []globbedResourceDir, rroDirs []rroDir, resZips android.Paths) { @@ -156,7 +188,7 @@ func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext, dir: dir, files: androidResourceGlob(ctx, dir), }) - resOverlayDirs, resRRODirs := overlayResourceGlob(ctx, dir) + resOverlayDirs, resRRODirs := overlayResourceGlob(ctx, a, dir) overlayDirs = append(overlayDirs, resOverlayDirs...) rroDirs = append(rroDirs, resRRODirs...) } @@ -412,14 +444,16 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati assets = append(assets, aarDep.ExportedAssets().Path()) } - outer: - for _, d := range aarDep.ExportedRRODirs() { - for _, e := range staticRRODirs { - if d.path == e.path { - continue outer + if !ctx.Config().EnforceRROExemptedForModule(ctx.ModuleName()) { + outer: + for _, d := range aarDep.ExportedRRODirs() { + for _, e := range staticRRODirs { + if d.path == e.path { + continue outer + } } + staticRRODirs = append(staticRRODirs, d) } - staticRRODirs = append(staticRRODirs, d) } } } @@ -621,6 +655,17 @@ func (a *AARImport) ExportedAssets() android.OptionalPath { return android.OptionalPath{} } +// RRO enforcement is not available on aar_import since its RRO dirs are not +// exported. +func (a *AARImport) SetRROEnforcedForDependent(enforce bool) { +} + +// RRO enforcement is not available on aar_import since its RRO dirs are not +// exported. +func (a *AARImport) IsRROEnforced(ctx android.BaseModuleContext) bool { + return false +} + func (a *AARImport) Prebuilt() *android.Prebuilt { return &a.prebuilt } diff --git a/java/android_resources.go b/java/android_resources.go index c2bc746ab..97f76793c 100644 --- a/java/android_resources.go +++ b/java/android_resources.go @@ -66,13 +66,13 @@ type globbedResourceDir struct { files android.Paths } -func overlayResourceGlob(ctx android.ModuleContext, dir android.Path) (res []globbedResourceDir, +func overlayResourceGlob(ctx android.ModuleContext, a *aapt, dir android.Path) (res []globbedResourceDir, rroDirs []rroDir) { overlayData := ctx.Config().Get(overlayDataKey).([]overlayGlobResult) // Runtime resource overlays (RRO) may be turned on by the product config for some modules - rroEnabled := ctx.Config().EnforceRROForModule(ctx.ModuleName()) + rroEnabled := a.IsRROEnforced(ctx) for _, data := range overlayData { files := data.paths.PathsInDirectory(filepath.Join(data.dir, dir.String())) diff --git a/java/app_test.go b/java/app_test.go index 4347db8b1..b7be1899c 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -848,19 +848,17 @@ func TestAndroidResources(t *testing.T) { "lib": { buildDir + "/.intermediates/lib2/android_common/package-res.apk", "lib/res/res/values/strings.xml", - "device/vendor/blah/overlay/lib/res/values/strings.xml", }, }, rroDirs: map[string][]string{ "foo": { "device:device/vendor/blah/overlay/foo/res", - // Enforce RRO on "foo" could imply RRO on static dependencies, but for now it doesn't. - // "device/vendor/blah/overlay/lib/res", "product:product/vendor/blah/overlay/foo/res", + "device:device/vendor/blah/overlay/lib/res", }, "bar": nil, - "lib": nil, + "lib": {"device:device/vendor/blah/overlay/lib/res"}, }, }, { @@ -3401,3 +3399,114 @@ func TestOverrideRuntimeResourceOverlay(t *testing.T) { checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag) } } + +func TestEnforceRRO_propagatesToDependencies(t *testing.T) { + testCases := []struct { + name string + enforceRROTargets []string + enforceRROExemptTargets []string + rroDirs map[string][]string + }{ + { + name: "no RRO", + enforceRROTargets: nil, + enforceRROExemptTargets: nil, + rroDirs: map[string][]string{ + "foo": nil, + "bar": nil, + }, + }, + { + name: "enforce RRO on all", + enforceRROTargets: []string{"*"}, + enforceRROExemptTargets: nil, + 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"}, + enforceRROExemptTargets: nil, + rroDirs: map[string][]string{ + "foo": {"product/vendor/blah/overlay/lib2/res"}, + "bar": {"product/vendor/blah/overlay/lib2/res"}, + }, + }, + { + name: "enforce RRO on foo, bar exempted", + enforceRROTargets: []string{"foo"}, + enforceRROExemptTargets: []string{"bar"}, + rroDirs: map[string][]string{ + "foo": {"product/vendor/blah/overlay/lib2/res"}, + "bar": nil, + }, + }, + } + + productResourceOverlays := []string{ + "product/vendor/blah/overlay", + } + + fs := map[string][]byte{ + "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) { + config := testAppConfig(nil, bp, fs) + config.TestProductVariables.ProductResourceOverlays = productResourceOverlays + if testCase.enforceRROTargets != nil { + config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets + } + if testCase.enforceRROExemptTargets != nil { + config.TestProductVariables.EnforceRROExemptedTargets = testCase.enforceRROExemptTargets + } + + ctx := testContext() + run(t, ctx, config) + + modules := []string{"foo", "bar"} + for _, moduleName := range modules { + module := ctx.ModuleForTests(moduleName, "android_common") + mkEntries := android.AndroidMkEntriesForTest(t, config, "", 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) + } + } + }) + } +} diff --git a/java/java_test.go b/java/java_test.go index f16639aa8..bb200d358 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -102,6 +102,10 @@ func testContext() *android.TestContext { dexpreopt.RegisterToolModulesForTest(ctx) + ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel() + }) + return ctx }