Install vintf fragments and init.rc files in Soong

Determine the install location of vintf fragments and init.rc files
in Soong so that they are available to Soong-generated module-info.json
entries.  Collect the vintf fragment and init.rc files requested by all Soong
modules, deduplicate the list, and install them in Soong.

Bug: 309006256
Test: Compare module-info.json
Change-Id: I491dc05a773d1a82e485475834d2669fc95cfa1e
diff --git a/android/makevars.go b/android/makevars.go
index d4cfd29..4039e7e 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -16,9 +16,11 @@
 
 import (
 	"bytes"
+	"cmp"
 	"fmt"
 	"path/filepath"
 	"runtime"
+	"slices"
 	"sort"
 	"strings"
 
@@ -242,6 +244,8 @@
 	var dists []dist
 	var phonies []phony
 	var katiInstalls []katiInstall
+	var katiInitRcInstalls []katiInstall
+	var katiVintfManifestInstalls []katiInstall
 	var katiSymlinks []katiInstall
 
 	providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
@@ -275,10 +279,33 @@
 
 		if m.ExportedToMake() {
 			katiInstalls = append(katiInstalls, m.base().katiInstalls...)
+			katiInitRcInstalls = append(katiInitRcInstalls, m.base().katiInitRcInstalls...)
+			katiVintfManifestInstalls = append(katiVintfManifestInstalls, m.base().katiVintfInstalls...)
 			katiSymlinks = append(katiSymlinks, m.base().katiSymlinks...)
 		}
 	})
 
+	compareKatiInstalls := func(a, b katiInstall) int {
+		aTo, bTo := a.to.String(), b.to.String()
+		if cmpTo := cmp.Compare(aTo, bTo); cmpTo != 0 {
+			return cmpTo
+		}
+
+		aFrom, bFrom := a.from.String(), b.from.String()
+		return cmp.Compare(aFrom, bFrom)
+	}
+
+	slices.SortFunc(katiInitRcInstalls, compareKatiInstalls)
+	katiInitRcInstalls = slices.CompactFunc(katiInitRcInstalls, func(a, b katiInstall) bool {
+		return compareKatiInstalls(a, b) == 0
+	})
+	katiInstalls = append(katiInstalls, katiInitRcInstalls...)
+
+	slices.SortFunc(katiVintfManifestInstalls, compareKatiInstalls)
+	katiVintfManifestInstalls = slices.CompactFunc(katiVintfManifestInstalls, func(a, b katiInstall) bool {
+		return compareKatiInstalls(a, b) == 0
+	})
+
 	if ctx.Failed() {
 		return
 	}
@@ -316,7 +343,7 @@
 		ctx.Errorf(err.Error())
 	}
 
-	installsBytes := s.writeInstalls(katiInstalls, katiSymlinks)
+	installsBytes := s.writeInstalls(katiInstalls, katiSymlinks, katiVintfManifestInstalls)
 	if err := pathtools.WriteFileIfChanged(installsFile, installsBytes, 0666); err != nil {
 		ctx.Errorf(err.Error())
 	}
@@ -438,7 +465,7 @@
 // writeInstalls writes the list of install rules generated by Soong to a makefile.  The rules
 // are exported to Make instead of written directly to the ninja file so that main.mk can add
 // the dependencies from the `required` property that are hard to resolve in Soong.
-func (s *makeVarsSingleton) writeInstalls(installs, symlinks []katiInstall) []byte {
+func (s *makeVarsSingleton) writeInstalls(installs, symlinks, katiVintfManifestInstalls []katiInstall) []byte {
 	buf := &bytes.Buffer{}
 
 	fmt.Fprint(buf, `# Autogenerated file
@@ -486,9 +513,9 @@
 	for _, symlink := range symlinks {
 		fmt.Fprintf(buf, "%s:", symlink.to.String())
 		if symlink.from != nil {
-			// The symlink doesn't need updating when the target is modified, but we sometimes
-			// have a dependency on a symlink to a binary instead of to the binary directly, and
-			// the mtime of the symlink must be updated when the binary is modified, so use a
+			// The katiVintfManifestInstall doesn't need updating when the target is modified, but we sometimes
+			// have a dependency on a katiVintfManifestInstall to a binary instead of to the binary directly, and
+			// the mtime of the katiVintfManifestInstall must be updated when the binary is modified, so use a
 			// normal dependency here instead of an order-only dependency.
 			fmt.Fprintf(buf, " %s", symlink.from.String())
 		}
@@ -507,7 +534,7 @@
 		if symlink.from != nil {
 			rel, err := filepath.Rel(filepath.Dir(symlink.to.String()), symlink.from.String())
 			if err != nil {
-				panic(fmt.Errorf("failed to find relative path for symlink from %q to %q: %w",
+				panic(fmt.Errorf("failed to find relative path for katiVintfManifestInstall from %q to %q: %w",
 					symlink.from.String(), symlink.to.String(), err))
 			}
 			fromStr = rel
@@ -521,6 +548,19 @@
 		fmt.Fprintln(buf)
 	}
 
+	for _, install := range katiVintfManifestInstalls {
+		// Write a rule for each vintf install request that calls the copy-vintf-manifest-chedk make function.
+		fmt.Fprintf(buf, "$(eval $(call copy-vintf-manifest-checked, %s, %s))\n", install.from.String(), install.to.String())
+
+		if len(install.implicitDeps) > 0 {
+			panic(fmt.Errorf("unsupported implicitDeps %q in vintf install rule %q", install.implicitDeps, install.to))
+		}
+		if len(install.orderOnlyDeps) > 0 {
+			panic(fmt.Errorf("unsupported orderOnlyDeps %q in vintf install rule %q", install.orderOnlyDeps, install.to))
+		}
+
+		fmt.Fprintln(buf)
+	}
 	return buf.Bytes()
 }