Create apkcerts.txt using Soong

This CL creates an apkcerts.txt of the apps installed on the device, and
any apk-in-apex of installed apexes. Note that this behavior is unlike
make. There is some ongoing discussion on whether the make behavior can
be updated to only list the installed apps as well.

To implement this, androidDevice will consult the following providers
from the list of installed modules
- AppInfoProvider (android_app, android_app_import, ...)
- AppInfosProvider (for apk-in-apex)
- RuntimeResourceOverlayInfoProvider (for checked-in and autogen rros)

Test: With https://r.android.com/3529333, apkcerts.txt is identical for
aosp_cf_x86_64_phone between make and soong

Bug: 399788149
Change-Id: I10009ea8761a197dd2301acf9615079bd28af3f5
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index 3d306e3..17209ed 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -114,6 +114,7 @@
 	miscInfo                    android.Path
 	rootDirForFsConfig          string
 	rootDirForFsConfigTimestamp android.Path
+	apkCertsInfo                android.Path
 }
 
 func AndroidDeviceFactory() android.Module {
@@ -188,6 +189,7 @@
 
 	allInstalledModules := a.allInstalledModules(ctx)
 
+	a.apkCertsInfo = a.buildApkCertsInfo(ctx, allInstalledModules)
 	a.kernelConfig, a.kernelVersion = a.extractKernelVersionAndConfigs(ctx)
 	a.miscInfo = a.addMiscInfo(ctx)
 	a.buildTargetFilesZip(ctx, allInstalledModules)
@@ -651,6 +653,9 @@
 	}
 	installedApexKeys = android.SortedUniquePaths(installedApexKeys) // Sort by keypath to match make
 	builder.Command().Text("cat").Inputs(installedApexKeys).Textf(" >> %s/META/apexkeys.txt", targetFilesDir.String())
+	// apkcerts.txt
+	builder.Command().Textf("cp").Input(a.apkCertsInfo).Textf(" %s/META/", targetFilesDir.String())
+
 	// Copy fastboot-info.txt
 	if fastbootInfo := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.FastbootInfo)); fastbootInfo != nil {
 		// TODO (b/399788523): Autogenerate fastboot-info.txt if there is no source fastboot-info.txt
@@ -864,3 +869,49 @@
 
 	return extractedVersionFile, extractedConfigsFile
 }
+
+func (a *androidDevice) buildApkCertsInfo(ctx android.ModuleContext, allInstalledModules []android.Module) android.Path {
+	// TODO (spandandas): Add compressed
+	formatLine := func(cert java.Certificate, name, partition string) string {
+		pem := cert.AndroidMkString()
+		var key string
+		if cert.Key == nil {
+			key = ""
+		} else {
+			key = cert.Key.String()
+		}
+		return fmt.Sprintf(`name="%s" certificate="%s" private_key="%s" partition="%s"`, name, pem, key, partition)
+	}
+
+	apkCerts := []string{}
+	for _, installedModule := range allInstalledModules {
+		partition := ""
+		if commonInfo, ok := android.OtherModuleProvider(ctx, installedModule, android.CommonModuleInfoKey); ok {
+			partition = commonInfo.PartitionTag
+		} else {
+			ctx.ModuleErrorf("%s does not set CommonModuleInfoKey", installedModule.Name())
+		}
+		if info, ok := android.OtherModuleProvider(ctx, installedModule, java.AppInfoProvider); ok {
+			apkCerts = append(apkCerts, formatLine(info.Certificate, info.InstallApkName+".apk", partition))
+		} else if info, ok := android.OtherModuleProvider(ctx, installedModule, java.AppInfosProvider); ok {
+			for _, certInfo := range info {
+				apkCerts = append(apkCerts, formatLine(certInfo.Certificate, certInfo.InstallApkName+".apk", partition))
+			}
+		} else if info, ok := android.OtherModuleProvider(ctx, installedModule, java.RuntimeResourceOverlayInfoProvider); ok {
+			apkCerts = append(apkCerts, formatLine(info.Certificate, info.OutputFile.Base(), partition))
+		}
+	}
+	slices.Sort(apkCerts) // sort by name
+	fsInfos := a.getFsInfos(ctx)
+	if fsInfos["system"].HasFsverity {
+		defaultPem, defaultKey := ctx.Config().DefaultAppCertificate(ctx)
+		apkCerts = append(apkCerts, formatLine(java.Certificate{Pem: defaultPem, Key: defaultKey}, "BuildManifest.apk", "system"))
+		if info, ok := fsInfos["system_ext"]; ok && info.HasFsverity {
+			apkCerts = append(apkCerts, formatLine(java.Certificate{Pem: defaultPem, Key: defaultKey}, "BuildManifestSystemExt.apk", "system_ext"))
+		}
+	}
+
+	apkCertsInfo := android.PathForModuleOut(ctx, "apkcerts.txt")
+	android.WriteFileRuleVerbatim(ctx, apkCertsInfo, strings.Join(apkCerts, "\n")+"\n")
+	return apkCertsInfo
+}