diff --git a/android/testing.go b/android/testing.go index 03572b300..fc58cecf1 100644 --- a/android/testing.go +++ b/android/testing.go @@ -131,7 +131,7 @@ func (m TestingModule) Output(file string) BuildParams { outputs = append(outputs, p.Output) } for _, f := range outputs { - if f.Rel() == file { + if f.String() == file || f.Rel() == file { return p } } diff --git a/java/androidmk.go b/java/androidmk.go index 86e000d7f..2e676399a 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -143,6 +143,13 @@ func (app *AndroidApp) AndroidMk() android.AndroidMkData { // framework_res. fmt.Fprintln(w, "LOCAL_NO_STANDARD_LIBRARIES := true") } + + if len(app.rroDirs) > 0 { + fmt.Fprintln(w, "LOCAL_SOONG_RRO_DIRS :=", strings.Join(app.rroDirs.Strings(), " ")) + } + fmt.Fprintln(w, "LOCAL_EXPORT_PACKAGE_RESOURCES :=", + Bool(app.appProperties.Export_package_resources)) + fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", app.manifestPath.String()) } }, }, diff --git a/java/app.go b/java/app.go index b66eb4b7d..6866e2a83 100644 --- a/java/app.go +++ b/java/app.go @@ -70,6 +70,8 @@ type AndroidApp struct { aaptSrcJar android.Path exportPackage android.Path + rroDirs android.Paths + manifestPath android.Path } func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { @@ -86,7 +88,7 @@ func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { } func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { - linkFlags, linkDeps, resDirs, overlayDirs := a.aapt2Flags(ctx) + linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, manifestPath := a.aapt2Flags(ctx) packageRes := android.PathForModuleOut(ctx, "package-res.apk") srcJar := android.PathForModuleGen(ctx, "R.jar") @@ -144,6 +146,8 @@ func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { CreateAppPackage(ctx, packageFile, a.exportPackage, a.outputFile, certificates) a.outputFile = packageFile + a.rroDirs = rroDirs + a.manifestPath = manifestPath if ctx.ModuleName() == "framework-res" { // framework-res.apk is installed as system/framework/framework-res.apk @@ -171,7 +175,7 @@ type globbedResourceDir struct { } func (a *AndroidApp) aapt2Flags(ctx android.ModuleContext) (flags []string, deps android.Paths, - resDirs, overlayDirs []globbedResourceDir) { + resDirs, overlayDirs []globbedResourceDir, rroDirs android.Paths, manifestPath android.Path) { hasVersionCode := false hasVersionName := false @@ -205,7 +209,9 @@ func (a *AndroidApp) aapt2Flags(ctx android.ModuleContext) (flags []string, deps dir: dir, files: resourceGlob(ctx, dir), }) - overlayDirs = append(overlayDirs, overlayResourceGlob(ctx, dir)...) + resOverlayDirs, resRRODirs := overlayResourceGlob(ctx, dir) + overlayDirs = append(overlayDirs, resOverlayDirs...) + rroDirs = append(rroDirs, resRRODirs...) } var assetFiles android.Paths @@ -221,7 +227,7 @@ func (a *AndroidApp) aapt2Flags(ctx android.ModuleContext) (flags []string, deps manifestFile = *a.properties.Manifest } - manifestPath := android.PathForModuleSrc(ctx, manifestFile) + manifestPath = android.PathForModuleSrc(ctx, manifestFile) linkFlags = append(linkFlags, "--manifest "+manifestPath.String()) linkDeps = append(linkDeps, manifestPath) @@ -288,7 +294,7 @@ func (a *AndroidApp) aapt2Flags(ctx android.ModuleContext) (flags []string, deps // TODO: LOCAL_PACKAGE_OVERRIDES // $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \ - return linkFlags, linkDeps, resDirs, overlayDirs + return linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, manifestPath } func AndroidAppFactory() android.Module { @@ -320,26 +326,49 @@ func resourceGlob(ctx android.ModuleContext, dir android.Path) android.Paths { type overlayGlobResult struct { dir string paths android.DirectorySortedPaths + + // Set to true of the product has selected that values in this overlay should not be moved to + // Runtime Resource Overlay (RRO) packages. + excludeFromRRO bool } const overlayDataKey = "overlayDataKey" -func overlayResourceGlob(ctx android.ModuleContext, dir android.Path) []globbedResourceDir { +func overlayResourceGlob(ctx android.ModuleContext, dir android.Path) (res []globbedResourceDir, + rroDirs android.Paths) { + overlayData := ctx.Config().Get(overlayDataKey).([]overlayGlobResult) - var ret []globbedResourceDir + // Runtime resource overlays (RRO) may be turned on by the product config for some modules + rroEnabled := false + enforceRROTargets := ctx.Config().ProductVariables.EnforceRROTargets + if enforceRROTargets != nil { + if len(*enforceRROTargets) == 1 && (*enforceRROTargets)[0] == "*" { + rroEnabled = true + } else if inList(ctx.ModuleName(), *enforceRROTargets) { + rroEnabled = true + } + } for _, data := range overlayData { files := data.paths.PathsInDirectory(filepath.Join(data.dir, dir.String())) if len(files) > 0 { - ret = append(ret, globbedResourceDir{ - dir: android.PathForSource(ctx, data.dir, dir.String()), - files: files, - }) + overlayModuleDir := android.PathForSource(ctx, data.dir, dir.String()) + // If enforce RRO is enabled for this module and this overlay is not in the + // exclusion list, ignore the overlay. The list of ignored overlays will be + // passed to Make to be turned into an RRO package. + if rroEnabled && !data.excludeFromRRO { + rroDirs = append(rroDirs, overlayModuleDir) + } else { + res = append(res, globbedResourceDir{ + dir: overlayModuleDir, + files: files, + }) + } } } - return ret + return res, rroDirs } func OverlaySingletonFactory() android.Singleton { @@ -349,10 +378,25 @@ func OverlaySingletonFactory() android.Singleton { type overlaySingleton struct{} func (overlaySingleton) GenerateBuildActions(ctx android.SingletonContext) { + + // Specific overlays may be excluded from Runtime Resource Overlays by the product config + var rroExcludedOverlays []string + if ctx.Config().ProductVariables.EnforceRROExcludedOverlays != nil { + rroExcludedOverlays = *ctx.Config().ProductVariables.EnforceRROExcludedOverlays + } + var overlayData []overlayGlobResult for _, overlay := range ctx.Config().ResourceOverlays() { var result overlayGlobResult result.dir = overlay + + // Mark overlays that will not have Runtime Resource Overlays enforced on them + for _, exclude := range rroExcludedOverlays { + if strings.HasPrefix(overlay, exclude) { + result.excludeFromRRO = true + } + } + files, err := ctx.GlobWithDeps(filepath.Join(overlay, "**/*"), aaptIgnoreFilenames) if err != nil { ctx.Errorf("failed to glob resource dir %q: %s", overlay, err.Error()) diff --git a/java/app_test.go b/java/app_test.go index 37489f587..35230d45f 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -91,3 +91,144 @@ func TestApp(t *testing.T) { expectedLinkImplicits, res.Implicits.Strings()) } } + +var testEnforceRROTests = []struct { + name string + enforceRROTargets []string + enforceRROExcludedOverlays []string + fooOverlayFiles []string + fooRRODirs []string + barOverlayFiles []string + barRRODirs []string +}{ + { + name: "no RRO", + enforceRROTargets: nil, + enforceRROExcludedOverlays: nil, + fooOverlayFiles: []string{ + "device/vendor/blah/overlay/foo/res/values/strings.xml", + "device/vendor/blah/static_overlay/foo/res/values/strings.xml", + }, + fooRRODirs: nil, + barOverlayFiles: []string{ + "device/vendor/blah/overlay/bar/res/values/strings.xml", + "device/vendor/blah/static_overlay/bar/res/values/strings.xml", + }, + barRRODirs: nil, + }, + { + name: "enforce RRO on foo", + enforceRROTargets: []string{"foo"}, + enforceRROExcludedOverlays: []string{"device/vendor/blah/static_overlay"}, + fooOverlayFiles: []string{ + "device/vendor/blah/static_overlay/foo/res/values/strings.xml", + }, + fooRRODirs: []string{ + "device/vendor/blah/overlay/foo/res", + }, + barOverlayFiles: []string{ + "device/vendor/blah/overlay/bar/res/values/strings.xml", + "device/vendor/blah/static_overlay/bar/res/values/strings.xml", + }, + barRRODirs: nil, + }, + { + name: "enforce RRO on all", + enforceRROTargets: []string{"*"}, + enforceRROExcludedOverlays: []string{"device/vendor/blah/static_overlay"}, + fooOverlayFiles: []string{ + "device/vendor/blah/static_overlay/foo/res/values/strings.xml", + }, + fooRRODirs: []string{ + "device/vendor/blah/overlay/foo/res", + }, + barOverlayFiles: []string{ + "device/vendor/blah/static_overlay/bar/res/values/strings.xml", + }, + barRRODirs: []string{ + "device/vendor/blah/overlay/bar/res", + }, + }, +} + +func TestEnforceRRO(t *testing.T) { + resourceOverlays := []string{ + "device/vendor/blah/overlay", + "device/vendor/blah/overlay2", + "device/vendor/blah/static_overlay", + } + + fs := map[string][]byte{ + "foo/res/res/values/strings.xml": nil, + "bar/res/res/values/strings.xml": nil, + "device/vendor/blah/overlay/foo/res/values/strings.xml": nil, + "device/vendor/blah/overlay/bar/res/values/strings.xml": nil, + "device/vendor/blah/static_overlay/foo/res/values/strings.xml": nil, + "device/vendor/blah/static_overlay/bar/res/values/strings.xml": nil, + "device/vendor/blah/overlay2/res/values/strings.xml": nil, + } + + bp := ` + android_app { + name: "foo", + resource_dirs: ["foo/res"], + } + + android_app { + name: "bar", + resource_dirs: ["bar/res"], + } + ` + + for _, testCase := range testEnforceRROTests { + t.Run(testCase.name, func(t *testing.T) { + config := testConfig(nil) + config.ProductVariables.ResourceOverlays = &resourceOverlays + if testCase.enforceRROTargets != nil { + config.ProductVariables.EnforceRROTargets = &testCase.enforceRROTargets + } + if testCase.enforceRROExcludedOverlays != nil { + config.ProductVariables.EnforceRROExcludedOverlays = &testCase.enforceRROExcludedOverlays + } + + ctx := testAppContext(config, bp, fs) + run(t, ctx, config) + + getOverlays := func(moduleName string) ([]string, []string) { + module := ctx.ModuleForTests(moduleName, "android_common") + overlayCompiledPaths := module.Output("aapt2/overlay.list").Inputs.Strings() + + var overlayFiles []string + for _, o := range overlayCompiledPaths { + overlayFiles = append(overlayFiles, module.Output(o).Inputs.Strings()...) + } + + rroDirs := module.Module().(*AndroidApp).rroDirs.Strings() + + return overlayFiles, rroDirs + } + + fooOverlayFiles, fooRRODirs := getOverlays("foo") + barOverlayFiles, barRRODirs := getOverlays("bar") + + if !reflect.DeepEqual(fooOverlayFiles, testCase.fooOverlayFiles) { + t.Errorf("expected foo overlay files:\n %#v\n got:\n %#v", + testCase.fooOverlayFiles, fooOverlayFiles) + } + if !reflect.DeepEqual(fooRRODirs, testCase.fooRRODirs) { + t.Errorf("expected foo rroDirs: %#v\n got:\n %#v", + testCase.fooRRODirs, fooRRODirs) + } + + if !reflect.DeepEqual(barOverlayFiles, testCase.barOverlayFiles) { + t.Errorf("expected bar overlay files:\n %#v\n got:\n %#v", + testCase.barOverlayFiles, barOverlayFiles) + } + if !reflect.DeepEqual(barRRODirs, testCase.barRRODirs) { + t.Errorf("expected bar rroDirs: %#v\n got:\n %#v", + testCase.barRRODirs, barRRODirs) + } + + }) + } +}