Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e2f98da5 authored by Spandan Das's avatar Spandan Das
Browse files

Introduce module type to autogenerate RROS

At ToT, soong emits metadata to make (LOCAL_SOONG_PRODUCT_RRO_DIRS and
LOCAL_SOONG_DEVICE_RRO_DIRS), and make uses this metadata to build and
install apks that are intalled in /vendor or /product. This CL ports
this logic to soong using a new module type. The module type will have a
dependency on the base module (e.g. SystemUI). This dependency edge will
be used to get information such as
- rroDirsDepSet
- manifest (to get package_name)

To reduce code duplication, `aaptBuildActions` has been extended to
accept a manifest and rroDirs parameter.

This should be a no op for now. The followup work includes
- Autogenerating these module in the load hook of android_app and
  override_android_app
- Including the autogenerated modules in kati built img files
- Including the autogenerated modules in soong built img files

Test: go test ./java
Bug: 375277835
Change-Id: Icd34f59eb751ba60db3c265acada946e20db5f26
parent 1a6f176c
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ func init() {
	AddNeverAllowRules(createFilesystemIsAutoGeneratedRule())
	AddNeverAllowRules(createKotlinPluginRule()...)
	AddNeverAllowRules(createPrebuiltEtcBpDefineRule())
	AddNeverAllowRules(createAutogenRroBpDefineRule())
}

// Add a NeverAllow rule to the set of rules to apply.
@@ -344,6 +345,15 @@ func createPrebuiltEtcBpDefineRule() Rule {
		Because("module type not allowed to be defined in bp file")
}

func createAutogenRroBpDefineRule() Rule {
	return NeverAllow().
		ModuleType(
			"autogen_runtime_resource_overlay",
		).
		DefinedInBpFile().
		Because("Module type will be autogenerated by soong. Use runtime_resource_overlay instead")
}

func neverallowMutator(ctx BottomUpMutatorContext) {
	m, ok := ctx.Module().(Module)
	if !ok {
+85 −11
Original line number Diff line number Diff line
@@ -417,6 +417,25 @@ type aaptBuildActionOptions struct {
	extraLinkFlags                 []string
	aconfigTextFiles               android.Paths
	usesLibrary                    *usesLibrary
	// If rroDirs is provided, it will be used to generate package-res.apk
	rroDirs *android.Paths
	// If manifestForAapt is not nil, it will be used for aapt instead of the default source manifest.
	manifestForAapt android.Path
}

func filterRRO(rroDirsDepSet depset.DepSet[rroDir], filter overlayType) android.Paths {
	var paths android.Paths
	seen := make(map[android.Path]bool)
	for _, d := range rroDirsDepSet.ToList() {
		if d.overlayType == filter {
			if seen[d.path] {
				continue
			}
			seen[d.path] = true
			paths = append(paths, d.path)
		}
	}
	return paths
}

func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptions) {
@@ -428,10 +447,15 @@ func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptio
	opts.classLoaderContexts = opts.classLoaderContexts.ExcludeLibs(opts.excludedLibs)

	// App manifest file
	var manifestFilePath android.Path
	if opts.manifestForAapt != nil {
		manifestFilePath = opts.manifestForAapt
	} else {
		manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
	manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
		manifestFilePath = android.PathForModuleSrc(ctx, manifestFile)
	}

	manifestPath := ManifestFixer(ctx, manifestSrcPath, ManifestFixerParams{
	manifestPath := ManifestFixer(ctx, manifestFilePath, ManifestFixerParams{
		SdkContext:                     opts.sdkContext,
		ClassLoaderContexts:            opts.classLoaderContexts,
		IsLibrary:                      a.isLibrary,
@@ -472,6 +496,10 @@ func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptio

	compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, opts.sdkContext, manifestPath)

	a.rroDirsDepSet = depset.NewBuilder[rroDir](depset.TOPOLOGICAL).
		Direct(rroDirs...).
		Transitive(staticRRODirsDepSet).Build()

	linkFlags = append(linkFlags, libFlags...)
	linkDeps = append(linkDeps, sharedExportPackages...)
	linkDeps = append(linkDeps, staticDeps.resPackages()...)
@@ -565,6 +593,11 @@ func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptio
			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)
	}

	var splitPackages android.WritablePaths
	var splits []split

@@ -591,10 +624,20 @@ func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptio
	if !a.isLibrary {
		transitiveAssets = android.ReverseSliceInPlace(staticDeps.assets())
	}
	if opts.rroDirs == nil { // link resources and overlay
		aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt,
			linkFlags, linkDeps, compiledRes, compiledOverlay, transitiveAssets, splitPackages,
			opts.aconfigTextFiles)
		ctx.CheckbuildFile(packageRes)
	} else { // link autogenerated rro
		if len(compiledRro) == 0 {
			return
		}
		aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt,
			linkFlags, linkDeps, compiledRro, compiledRroOverlay, nil, nil,
			opts.aconfigTextFiles)
		ctx.CheckbuildFile(packageRes)
	}

	// Extract assets from the resource package output so that they can be used later in aapt2link
	// for modules that depend on this one.
@@ -652,15 +695,46 @@ func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptio
			usedResourceProcessor: a.useResourceProcessorBusyBox(ctx),
		}).
		Transitive(staticResourcesNodesDepSet).Build()
	a.rroDirsDepSet = depset.NewBuilder[rroDir](depset.TOPOLOGICAL).
		Direct(rroDirs...).
		Transitive(staticRRODirsDepSet).Build()
	a.manifestsDepSet = depset.NewBuilder[android.Path](depset.TOPOLOGICAL).
		Direct(a.manifestPath).
		DirectSlice(additionalManifests).
		Transitive(staticManifestsDepSet).Build()
}

// comileResInDir finds the resource files in dirs by globbing and then compiles them using aapt2
// returns the file paths of compiled resources
// dirs[0] is used as compileRes
// dirs[1:] is used as compileOverlay
func (a *aapt) compileResInDir(ctx android.ModuleContext, dirs android.Paths, compileFlags []string, aconfig android.Paths) (android.Paths, android.Paths) {
	filesInDir := func(dir android.Path) android.Paths {
		files, err := ctx.GlobWithDeps(filepath.Join(dir.String(), "**/*"), androidResourceIgnoreFilenames)
		if err != nil {
			ctx.ModuleErrorf("failed to glob overlay resource dir %q: %s", dir, err.Error())
			return nil
		}
		var filePaths android.Paths
		for _, file := range files {
			if strings.HasSuffix(file, "/") {
				continue // ignore directories
			}
			filePaths = append(filePaths, android.PathForSource(ctx, file))
		}
		return filePaths
	}

	var compiledRes, compiledOverlay android.Paths
	if len(dirs) == 0 {
		return nil, nil
	}
	compiledRes = append(compiledRes, aapt2Compile(ctx, dirs[0], filesInDir(dirs[0]), compileFlags, a.filterProduct(), aconfig).Paths()...)
	if len(dirs) > 0 {
		for _, dir := range dirs[1:] {
			compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir, filesInDir(dir), compileFlags, a.filterProduct(), aconfig).Paths()...)
		}
	}
	return compiledRes, compiledOverlay
}

var resourceProcessorBusyBox = pctx.AndroidStaticRule("resourceProcessorBusyBox",
	blueprint.RuleParams{
		Command: "${config.JavaCmd} -cp ${config.ResourceProcessorBusyBox} " +
@@ -805,7 +879,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext,
		switch depTag {
		case instrumentationForTag:
			// Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
		case sdkLibTag, libTag:
		case sdkLibTag, libTag, rroDepTag:
			if exportPackage != nil {
				sharedResourcesNodeDepSets = append(sharedResourcesNodeDepSets, aarDep.ResourcesNodeDepSet())
				sharedLibs = append(sharedLibs, exportPackage)
+18 −0
Original line number Diff line number Diff line
@@ -425,6 +425,24 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries {
	}
}

func (a *AutogenRuntimeResourceOverlay) AndroidMkEntries() []android.AndroidMkEntries {
	if a.IsHideFromMake() || a.outputFile == nil {
		return []android.AndroidMkEntries{android.AndroidMkEntries{
			Disabled: true,
		}}
	}
	return []android.AndroidMkEntries{android.AndroidMkEntries{
		Class:      "APPS",
		OutputFile: android.OptionalPathForPath(a.outputFile),
		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
			},
		},
	}}
}

func (a *AndroidApp) getOverriddenPackages() []string {
	var overridden []string
	if len(a.overridableAppProperties.Overrides) > 0 {
+144 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ package java
import (
	"android/soong/android"

	"github.com/google/blueprint"
	"github.com/google/blueprint/proptools"
)

@@ -29,6 +30,7 @@ func init() {

func RegisterRuntimeResourceOverlayBuildComponents(ctx android.RegistrationContext) {
	ctx.RegisterModuleType("runtime_resource_overlay", RuntimeResourceOverlayFactory)
	ctx.RegisterModuleType("autogen_runtime_resource_overlay", AutogenRuntimeResourceOverlayFactory)
	ctx.RegisterModuleType("override_runtime_resource_overlay", OverrideRuntimeResourceOverlayModuleFactory)
}

@@ -269,3 +271,145 @@ func OverrideRuntimeResourceOverlayModuleFactory() android.Module {
	android.InitOverrideModule(m)
	return m
}

var (
	generateOverlayManifestFile = pctx.AndroidStaticRule("generate_overlay_manifest",
		blueprint.RuleParams{
			Command: "build/make/tools/generate-enforce-rro-android-manifest.py " +
				"--package-info $in " +
				"--partition ${partition} " +
				"--priority ${priority} -o $out",
			CommandDeps: []string{"build/make/tools/generate-enforce-rro-android-manifest.py"},
		}, "partition", "priority",
	)
)

type AutogenRuntimeResourceOverlay struct {
	android.ModuleBase
	aapt

	properties AutogenRuntimeResourceOverlayProperties

	outputFile android.Path
}

type AutogenRuntimeResourceOverlayProperties struct {
	Base        *string
	Sdk_version *string
	Manifest    *string `android:"path"`
}

func AutogenRuntimeResourceOverlayFactory() android.Module {
	m := &AutogenRuntimeResourceOverlay{}
	m.AddProperties(&m.properties)
	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)

	return m
}

type rroDependencyTag struct {
	blueprint.DependencyTag
}

// Autogenerated RROs should always depend on the source android_app that created it.
func (tag rroDependencyTag) ReplaceSourceWithPrebuilt() bool {
	return false
}

var rroDepTag = rroDependencyTag{}

func (a *AutogenRuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
	sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
	if sdkDep.hasFrameworkLibs() {
		a.aapt.deps(ctx, sdkDep)
	}
	ctx.AddDependency(ctx.Module(), rroDepTag, proptools.String(a.properties.Base))
}

func (a *AutogenRuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
	if !a.Enabled(ctx) {
		return
	}
	var rroDirs android.Paths
	// Get rro dirs of the base app
	ctx.VisitDirectDepsWithTag(rroDepTag, func(m android.Module) {
		aarDep, _ := m.(AndroidLibraryDependency)
		if ctx.InstallInProduct() {
			rroDirs = filterRRO(aarDep.RRODirsDepSet(), product)
		} else {
			rroDirs = filterRRO(aarDep.RRODirsDepSet(), device)
		}
	})

	if len(rroDirs) == 0 {
		return
	}

	// Generate a manifest file
	genManifest := android.PathForModuleGen(ctx, "AndroidManifest.xml")
	partition := "vendor"
	priority := "0"
	if ctx.InstallInProduct() {
		partition = "product"
		priority = "1"
	}
	ctx.Build(pctx, android.BuildParams{
		Rule:   generateOverlayManifestFile,
		Input:  android.PathForModuleSrc(ctx, proptools.String(a.properties.Manifest)),
		Output: genManifest,
		Args: map[string]string{
			"partition": partition,
			"priority":  priority,
		},
	})

	// Compile and link resources into package-res.apk
	a.aapt.hasNoCode = true
	aaptLinkFlags := []string{"--auto-add-overlay", "--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}

	a.aapt.buildActions(ctx,
		aaptBuildActionOptions{
			sdkContext:      a,
			extraLinkFlags:  aaptLinkFlags,
			rroDirs:         &rroDirs,
			manifestForAapt: genManifest,
		},
	)

	if a.exportPackage == nil {
		return
	}
	// Sign the built package
	_, certificates := processMainCert(a.ModuleBase, "", nil, ctx)
	signed := android.PathForModuleOut(ctx, "signed", a.Name()+".apk")
	SignAppPackage(ctx, signed, a.exportPackage, certificates, nil, nil, "")
	a.outputFile = signed

	// Install the signed apk
	installDir := android.PathForModuleInstall(ctx, "overlay")
	ctx.InstallFile(installDir, signed.Base(), signed)
}

func (a *AutogenRuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
	return android.SdkSpecFrom(ctx, String(a.properties.Sdk_version))
}

func (a *AutogenRuntimeResourceOverlay) SystemModules() string {
	return ""
}

func (a *AutogenRuntimeResourceOverlay) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
	return a.SdkVersion(ctx).ApiLevel
}

func (r *AutogenRuntimeResourceOverlay) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel {
	return android.SdkSpecPrivate.ApiLevel
}

func (a *AutogenRuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
	return a.SdkVersion(ctx).ApiLevel
}

func (a *AutogenRuntimeResourceOverlay) InstallInProduct() bool {
	return a.ProductSpecific()
}