Define unstable container

Unstable container marks modules that are static reverse dependencies of
"framework-minus-apex". These modules contain unstable implementation
details of the platform, and should not be accessed by the CTS tests.

Test: m nothing
Bug: 338660802
Change-Id: Ia0e1b3070f0f7b2c1b9c27306323e2428d9722ca
diff --git a/android/container.go b/android/container.go
index 1a80418..10aff4d 100644
--- a/android/container.go
+++ b/android/container.go
@@ -178,13 +178,40 @@
 	return false
 }
 
+type unstableInfo struct {
+	// Determines if the module contains the private APIs of the platform.
+	ContainsPlatformPrivateApis bool
+}
+
+var unstableInfoProvider = blueprint.NewProvider[unstableInfo]()
+
+func determineUnstableModule(mctx ModuleContext) bool {
+	module := mctx.Module()
+	unstableModule := module.Name() == "framework-minus-apex"
+	if installable, ok := module.(InstallableModule); ok {
+		for _, staticDepTag := range installable.StaticDependencyTags() {
+			mctx.VisitDirectDepsWithTag(staticDepTag, func(dep Module) {
+				if unstableInfo, ok := OtherModuleProvider(mctx, dep, unstableInfoProvider); ok {
+					unstableModule = unstableModule || unstableInfo.ContainsPlatformPrivateApis
+				}
+			})
+		}
+	}
+	return unstableModule
+}
+
+var unstableContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	return determineUnstableModule(mctx)
+}
+
 // Map of [*container] to the [containerBoundaryFunc]
 var containerBoundaryFunctionsTable = map[*container]containerBoundaryFunc{
-	VendorContainer:  vendorContainerBoundaryFunc,
-	SystemContainer:  systemContainerBoundaryFunc,
-	ProductContainer: productContainerBoundaryFunc,
-	ApexContainer:    apexContainerBoundaryFunc,
-	CtsContainer:     ctsContainerBoundaryFunc,
+	VendorContainer:   vendorContainerBoundaryFunc,
+	SystemContainer:   systemContainerBoundaryFunc,
+	ProductContainer:  productContainerBoundaryFunc,
+	ApexContainer:     apexContainerBoundaryFunc,
+	CtsContainer:      ctsContainerBoundaryFunc,
+	UnstableContainer: unstableContainerBoundaryFunc,
 }
 
 // ----------------------------------------------------------------------------
@@ -264,11 +291,12 @@
 		name: "cts",
 		restricted: []restriction{
 			{
-				dependency: SystemContainer,
-				errorMessage: "CTS module should not depend on the modules belonging to the " +
-					"system partition, including \"framework\". Depending on the system " +
-					"partition may lead to disclosure of implementation details and regression " +
-					"due to API changes across platform versions. Try depending on the stubs instead.",
+				dependency: UnstableContainer,
+				errorMessage: "CTS module should not depend on the modules that contain the " +
+					"platform implementation details, including \"framework\". Depending on these " +
+					"modules may lead to disclosure of implementation details and regression " +
+					"due to API changes across platform versions. Try depending on the stubs instead " +
+					"and ensure that the module sets an appropriate 'sdk_version'.",
 				allowedExceptions: []exceptionHandleFuncLabel{
 					checkStubs,
 					checkNotStaticOrDynamicDepTag,
@@ -278,12 +306,19 @@
 		},
 	}
 
+	// Container signifying that the module contains unstable platform private APIs
+	UnstableContainer = &container{
+		name:       "unstable",
+		restricted: nil,
+	}
+
 	allContainers = []*container{
 		VendorContainer,
 		SystemContainer,
 		ProductContainer,
 		ApexContainer,
 		CtsContainer,
+		UnstableContainer,
 	}
 )
 
@@ -385,6 +420,12 @@
 }
 
 func setContainerInfo(ctx ModuleContext) {
+	// Required to determine the unstable container. This provider is set here instead of the
+	// unstableContainerBoundaryFunc in order to prevent setting the provider multiple times.
+	SetProvider(ctx, unstableInfoProvider, unstableInfo{
+		ContainsPlatformPrivateApis: determineUnstableModule(ctx),
+	})
+
 	if _, ok := ctx.Module().(InstallableModule); ok {
 		containersInfo := generateContainerInfo(ctx)
 		ctx.Module().base().containersInfo = containersInfo