Allow stubs implementation to be omitted

When defining a stubs library, allow specifying that the
implementation library does not need to be installed. This allows for
cases where the implementation is deployed in some non-standard way -
e.g. inside a Microdroid virtual machine.

Without this, we get build errors like: "TARGET module
com.android.compos requires non-existent TARGET module: libvm_payload".

Default behavior is unchanged. The change is protected by an allowlist
to limit usage to the immediate use case.

Bug: 243512108
Test: builds; soong tests pass
Test: Remove allowlist, see build failure
Change-Id: Iaae75f2e93b842f5944a7518cc95069d62c5a638
diff --git a/android/neverallow.go b/android/neverallow.go
index 293bac8..ad9880a 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -59,6 +59,7 @@
 	AddNeverAllowRules(createInitFirstStageRules()...)
 	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
 	AddNeverAllowRules(createBp2BuildRule())
+	AddNeverAllowRules(createCcStubsRule())
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -214,6 +215,17 @@
 	}
 }
 
+func createCcStubsRule() Rule {
+	ccStubsImplementationInstallableProjectsAllowedList := []string{
+		"packages/modules/Virtualization/vm_payload",
+	}
+
+	return NeverAllow().
+		NotIn(ccStubsImplementationInstallableProjectsAllowedList...).
+		WithMatcher("stubs.implementation_installable", isSetMatcherInstance).
+		Because("implementation_installable can only be used in allowed projects.")
+}
+
 func createUncompressDexRules() []Rule {
 	return []Rule{
 		NeverAllow().
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 4772799..5f5f9a1 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -367,6 +367,22 @@
 			"framework can't be used when building against SDK",
 		},
 	},
+	// Test for the rule restricting use of implementation_installable
+	{
+		name: `"implementation_installable" outside allowed list`,
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+				cc_library {
+					name: "outside_allowed_list",
+					stubs: {
+                                                implementation_installable: true,
+					},
+				}`),
+		},
+		expectedErrors: []string{
+			`module "outside_allowed_list": violates neverallow`,
+		},
+	},
 }
 
 var prepareForNeverAllowTest = GroupFixturePreparers(
@@ -419,6 +435,10 @@
 	Platform struct {
 		Shared_libs []string
 	}
+
+	Stubs struct {
+		Implementation_installable *bool
+	}
 }
 
 type mockCcLibraryModule struct {
diff --git a/apex/apex.go b/apex/apex.go
index 04808c1..b1b4e47 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2301,7 +2301,7 @@
 				//
 				// Always include if we are a host-apex however since those won't have any
 				// system libraries.
-				if !am.DirectlyInAnyApex() {
+				if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() {
 					// we need a module name for Make
 					name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
 					if !android.InList(name, a.requiredDeps) {
diff --git a/cc/cc.go b/cc/cc.go
index 306e483..c83ed71 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1376,6 +1376,13 @@
 	return false
 }
 
+func (c *Module) IsStubsImplementationRequired() bool {
+	if lib := c.library; lib != nil {
+		return lib.isStubsImplementationRequired()
+	}
+	return false
+}
+
 // If this is a stubs library, ImplementationModuleName returns the name of the module that contains
 // the implementation.  If it is an implementation library it returns its own name.
 func (c *Module) ImplementationModuleName(ctx android.BaseModuleContext) string {
diff --git a/cc/library.go b/cc/library.go
index b639930..0729ff4 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -71,6 +71,12 @@
 		// List versions to generate stubs libs for. The version name "current" is always
 		// implicitly added.
 		Versions []string
+
+		// Whether to not require the implementation of the library to be installed if a
+		// client of the stubs is installed. Defaults to true; set to false if the
+		// implementation is made available by some other means, e.g. in a Microdroid
+		// virtual machine.
+		Implementation_installable *bool
 	}
 
 	// set the name of the output
@@ -1339,6 +1345,7 @@
 	buildStubs() bool
 	setBuildStubs(isLatest bool)
 	hasStubsVariants() bool
+	isStubsImplementationRequired() bool
 	setStubsVersion(string)
 	stubsVersion() string
 
@@ -2298,6 +2305,10 @@
 		len(library.Properties.Stubs.Versions) > 0
 }
 
+func (library *libraryDecorator) isStubsImplementationRequired() bool {
+	return BoolDefault(library.Properties.Stubs.Implementation_installable, true)
+}
+
 func (library *libraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
 	if !library.hasStubsVariants() {
 		return nil