Add TransitivePackagingSpecs

Add TransitivePackagingSpecs to return the PackagingSpecs for a
module and any of its transitive dependencies that have dependency
tags for which IsInstallDepNeeded returns true.

Bug: 124313442
Test: m checkbuild
Change-Id: I1d6750db830d1601d696349674f0b7071372ca11
diff --git a/android/module.go b/android/module.go
index b9121e8..bbdeb27 100644
--- a/android/module.go
+++ b/android/module.go
@@ -441,6 +441,10 @@
 
 	FilesToInstall() InstallPaths
 	PackagingSpecs() []PackagingSpec
+
+	// TransitivePackagingSpecs returns the PackagingSpecs for this module and any transitive
+	// dependencies with dependency tags for which IsInstallDepNeeded() returns true.
+	TransitivePackagingSpecs() []PackagingSpec
 }
 
 // Qualified id for a module
@@ -1003,13 +1007,14 @@
 	// The primary visibility property, may be nil, that controls access to the module.
 	primaryVisibilityProperty visibilityProperty
 
-	noAddressSanitizer bool
-	installFiles       InstallPaths
-	installFilesDepSet *installPathsDepSet
-	checkbuildFiles    Paths
-	packagingSpecs     []PackagingSpec
-	noticeFiles        Paths
-	phonies            map[string]Paths
+	noAddressSanitizer   bool
+	installFiles         InstallPaths
+	installFilesDepSet   *installPathsDepSet
+	checkbuildFiles      Paths
+	packagingSpecs       []PackagingSpec
+	packagingSpecsDepSet *packagingSpecsDepSet
+	noticeFiles          Paths
+	phonies              map[string]Paths
 
 	// The files to copy to the dist as explicitly specified in the .bp file.
 	distFiles TaggedDistFiles
@@ -1339,15 +1344,17 @@
 
 // computeInstallDeps finds the installed paths of all dependencies that have a dependency
 // tag that is annotated as needing installation via the IsInstallDepNeeded method.
-func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) []*installPathsDepSet {
+func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSet, []*packagingSpecsDepSet) {
 	var installDeps []*installPathsDepSet
+	var packagingSpecs []*packagingSpecsDepSet
 	ctx.VisitDirectDeps(func(dep Module) {
 		if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) {
 			installDeps = append(installDeps, dep.base().installFilesDepSet)
+			packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet)
 		}
 	})
 
-	return installDeps
+	return installDeps, packagingSpecs
 }
 
 func (m *ModuleBase) FilesToInstall() InstallPaths {
@@ -1358,6 +1365,10 @@
 	return m.packagingSpecs
 }
 
+func (m *ModuleBase) TransitivePackagingSpecs() []PackagingSpec {
+	return m.packagingSpecsDepSet.ToList()
+}
+
 func (m *ModuleBase) NoAddressSanitizer() bool {
 	return m.noAddressSanitizer
 }
@@ -1587,7 +1598,7 @@
 		variables:         make(map[string]string),
 	}
 
-	dependencyInstallFiles := m.computeInstallDeps(ctx)
+	dependencyInstallFiles, dependencyPackagingSpecs := m.computeInstallDeps(ctx)
 	// set m.installFilesDepSet to only the transitive dependencies to be used as the dependencies
 	// of installed files of this module.  It will be replaced by a depset including the installed
 	// files of this module at the end for use by modules that depend on this one.
@@ -1702,6 +1713,7 @@
 	}
 
 	m.installFilesDepSet = newInstallPathsDepSet(m.installFiles, dependencyInstallFiles)
+	m.packagingSpecsDepSet = newPackagingSpecsDepSet(m.packagingSpecs, dependencyPackagingSpecs)
 
 	m.buildParams = ctx.buildParams
 	m.ruleParams = ctx.ruleParams
diff --git a/android/packaging.go b/android/packaging.go
index 09432e6..da745ff 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -203,3 +203,23 @@
 	builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
 	return entries
 }
+
+// packagingSpecsDepSet is a thin type-safe wrapper around the generic depSet.  It always uses
+// topological order.
+type packagingSpecsDepSet struct {
+	depSet
+}
+
+// newPackagingSpecsDepSet returns an immutable packagingSpecsDepSet with the given direct and
+// transitive contents.
+func newPackagingSpecsDepSet(direct []PackagingSpec, transitive []*packagingSpecsDepSet) *packagingSpecsDepSet {
+	return &packagingSpecsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
+}
+
+// ToList returns the packagingSpecsDepSet flattened to a list in topological order.
+func (d *packagingSpecsDepSet) ToList() []PackagingSpec {
+	if d == nil {
+		return nil
+	}
+	return d.depSet.ToList().([]PackagingSpec)
+}