Add enforced RRO support to Soong
Ignore overlay directories that have been selected for enforced RRO
by the product, and pass them to Make instead to be converted to
an auto generated RRO package.
Bug: 69917341
Test: m checkbuild
Change-Id: I8e2677f4c600acdd8dee0869bf4fbc3d5dbc8b44
diff --git a/java/androidmk.go b/java/androidmk.go
index 86e000d..2e67639 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -143,6 +143,13 @@
// 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 b66eb4b..6866e2a 100644
--- a/java/app.go
+++ b/java/app.go
@@ -70,6 +70,8 @@
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) 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 @@
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 @@
}
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 37489f5..35230d4 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -91,3 +91,144 @@
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)
+ }
+
+ })
+ }
+}