Install VINTF fragment modules in the APEX

Currently APEX ignores vintf_fragments modules within the APEX, and all
VINTF fragments are installed with prebuilt_etc modules. This change
installs vintf_fragment modules in the APEX, so any additional
prebuilt_etc modules are not required as long as binaries define
vintf_fragment_modules references.

Bug: 363105686
Test: AOSP CF x86_64 build succeeded
Change-Id: I33b58178cd51dc2c4bb9226fdd68e7990dccd6a2
Merged-In: I33b58178cd51dc2c4bb9226fdd68e7990dccd6a2
diff --git a/android/module.go b/android/module.go
index 80275a3..3295e93 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1091,6 +1091,10 @@
 	InstallAlwaysNeededDependencyTag
 }{}
 
+func IsVintfDepTag(depTag blueprint.DependencyTag) bool {
+	return depTag == vintfDepTag
+}
+
 func addVintfFragmentDeps(ctx BottomUpMutatorContext) {
 	// Vintf manifests in the recovery partition will be ignored.
 	if !ctx.Device() || ctx.Module().InstallInRecovery() {
@@ -1109,7 +1113,7 @@
 			// of nil pointer dereference errors, but we should resolve the missing dependencies.
 			continue
 		}
-		if vintfModule, ok := vintf.(*vintfFragmentModule); ok {
+		if vintfModule, ok := vintf.(*VintfFragmentModule); ok {
 			vintfPartition := vintfModule.PartitionTag(deviceConfig)
 			if modPartition != vintfPartition {
 				ctx.ModuleErrorf("Module %q(%q) and Vintf_fragment %q(%q) are installed to different partitions.",
diff --git a/android/vintf_fragment.go b/android/vintf_fragment.go
index a3343fd..85beb72 100644
--- a/android/vintf_fragment.go
+++ b/android/vintf_fragment.go
@@ -19,8 +19,9 @@
 	Src string `android:"path"`
 }
 
-type vintfFragmentModule struct {
+type VintfFragmentModule struct {
 	ModuleBase
+	ApexModuleBase
 
 	properties vintfFragmentProperties
 
@@ -40,7 +41,7 @@
 // Vintf fragment files formerly listed in vintf_fragment property would be transformed into
 // this module type.
 func vintfLibraryFactory() Module {
-	m := &vintfFragmentModule{}
+	m := &VintfFragmentModule{}
 	m.AddProperties(
 		&m.properties,
 	)
@@ -49,7 +50,7 @@
 	return m
 }
 
-func (m *vintfFragmentModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+func (m *VintfFragmentModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 	builder := NewRuleBuilder(pctx, ctx)
 	srcVintfFragment := PathForModuleSrc(ctx, m.properties.Src)
 	processedVintfFragment := PathForModuleOut(ctx, srcVintfFragment.Base())
@@ -69,8 +70,12 @@
 	ctx.InstallFile(m.installDirPath, processedVintfFragment.Base(), processedVintfFragment)
 }
 
+func (m *VintfFragmentModule) OutputFile() Path {
+	return m.outputFilePath
+}
+
 // Make this module visible to AndroidMK so it can be referenced from modules defined from Android.mk files
-func (m *vintfFragmentModule) AndroidMkEntries() []AndroidMkEntries {
+func (m *VintfFragmentModule) AndroidMkEntries() []AndroidMkEntries {
 	return []AndroidMkEntries{{
 		Class:      "ETC",
 		OutputFile: OptionalPathForPath(m.outputFilePath),
@@ -82,3 +87,11 @@
 		},
 	}}
 }
+
+var _ ApexModule = (*VintfFragmentModule)(nil)
+
+// Implements android.ApexModule
+func (m *VintfFragmentModule) ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion ApiLevel) error {
+	// VintfFragmetModule is independent from the SDK version.
+	return nil
+}
diff --git a/apex/apex.go b/apex/apex.go
index fa796e5..cb9dc49 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1505,6 +1505,12 @@
 	return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, config)
 }
 
+func apexFileForVintfFragment(ctx android.BaseModuleContext, vintfFragment *android.VintfFragmentModule) apexFile {
+	dirInApex := filepath.Join("etc", "vintf")
+
+	return newApexFile(ctx, vintfFragment.OutputFile(), vintfFragment.BaseModuleName(), dirInApex, etc, vintfFragment)
+}
+
 // javaModule is an interface to handle all Java modules (java_library, dex_import, etc) in the same
 // way.
 type javaModule interface {
@@ -2196,7 +2202,13 @@
 		// nothing
 	} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
 		ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
+	} else if android.IsVintfDepTag(depTag) {
+		if vf, ok := child.(*android.VintfFragmentModule); ok {
+			apexFile := apexFileForVintfFragment(ctx, vf)
+			vctx.filesInfo = append(vctx.filesInfo, apexFile)
+		}
 	}
+
 	return false
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 987cb69..8515792 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -12208,3 +12208,35 @@
 	fileList := android.ContentFromFileRuleForTests(t, result, partition.Output("fileList"))
 	android.AssertDeepEquals(t, "filesystem with apex", "apex/myapex.apex\n", fileList)
 }
+
+func TestVintfFragmentInApex(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, apex_default_bp+`
+		apex {
+			name: "myapex",
+			manifest: ":myapex.manifest",
+			androidManifest: ":myapex.androidmanifest",
+			key: "myapex.key",
+			binaries: [ "mybin" ],
+			updatable: false,
+		}
+
+		cc_binary {
+			name: "mybin",
+			srcs: ["mybin.cpp"],
+			vintf_fragment_modules: ["my_vintf_fragment.xml"],
+			apex_available: [ "myapex" ],
+		}
+
+		vintf_fragment {
+			name: "my_vintf_fragment.xml",
+			src: "my_vintf_fragment.xml",
+		}
+	`)
+
+	generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("generateFsConfig")
+	cmd := generateFsRule.RuleParams.Command
+
+	// Ensure that vintf fragment file is being installed
+	ensureContains(t, cmd, "/etc/vintf/my_vintf_fragment.xml ")
+}
diff --git a/apex/builder.go b/apex/builder.go
index b40fd09..63efa63 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -528,9 +528,10 @@
 	})
 }
 
-func isVintfFragment(fi apexFile) bool {
+func shouldApplyAssembleVintf(fi apexFile) bool {
 	isVintfFragment, _ := path.Match("etc/vintf/*", fi.path())
-	return isVintfFragment
+	_, fromVintfFragmentModule := fi.module.(*android.VintfFragmentModule)
+	return isVintfFragment && !fromVintfFragmentModule
 }
 
 func runAssembleVintf(ctx android.ModuleContext, vintfFragment android.Path) android.Path {
@@ -639,7 +640,7 @@
 			copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
 		} else {
 			// Copy the file into APEX
-			if !a.testApex && isVintfFragment(fi) {
+			if !a.testApex && shouldApplyAssembleVintf(fi) {
 				// copy the output of assemble_vintf instead of the original
 				vintfFragment := runAssembleVintf(ctx, fi.builtFile)
 				copyCommands = append(copyCommands, "cp -f "+vintfFragment.String()+" "+destPath)