Validate aconfig libs are built with the correct modes.

Bug: 323071835
Test: Unit tests and manual tests.
Change-Id: I32de90826c7c8bb4d8495608e959d554820ab9a2
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
index 74c1a5e..fcc57e1 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"io"
+	"maps"
 	"reflect"
 
 	"github.com/google/blueprint"
@@ -50,6 +51,35 @@
 
 var AconfigTransitiveDeclarationsInfoProvider = blueprint.NewProvider[AconfigTransitiveDeclarationsInfo]()
 
+type ModeInfo struct {
+	Container string
+	Mode      string
+}
+type CodegenInfo struct {
+	// AconfigDeclarations is the name of the aconfig_declarations modules that
+	// the codegen module is associated with
+	AconfigDeclarations []string
+
+	// Paths to the cache files of the associated aconfig_declaration modules
+	IntermediateCacheOutputPaths Paths
+
+	// Paths to the srcjar files generated from the java_aconfig_library modules
+	Srcjars Paths
+
+	ModeInfos map[string]ModeInfo
+}
+
+var CodegenInfoProvider = blueprint.NewProvider[CodegenInfo]()
+
+func propagateModeInfos(ctx ModuleContext, module Module, to, from map[string]ModeInfo) {
+	if len(from) > 0 {
+		depTag := ctx.OtherModuleDependencyTag(module)
+		if tag, ok := depTag.(PropagateAconfigValidationDependencyTag); ok && tag.PropagateAconfigValidation() {
+			maps.Copy(to, from)
+		}
+	}
+}
+
 // CollectDependencyAconfigFiles is used by some module types to provide finer dependency graphing than
 // we can do in ModuleBase.
 func CollectDependencyAconfigFiles(ctx ModuleContext, mergedAconfigFiles *map[string]Paths) {
@@ -90,13 +120,40 @@
 
 type aconfigPropagatingDeclarationsInfo struct {
 	AconfigFiles map[string]Paths
+	ModeInfos    map[string]ModeInfo
 }
 
 var aconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]()
 
+func VerifyAconfigBuildMode(ctx ModuleContext, container string, module blueprint.Module, asError bool) {
+	if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok {
+		for k, v := range dep.ModeInfos {
+			msg := fmt.Sprintf("%s/%s depends on %s/%s/%s across containers\n",
+				module.Name(), container, k, v.Container, v.Mode)
+			if v.Container != container && v.Mode != "exported" && v.Mode != "force-read-only" {
+				if asError {
+					ctx.ModuleErrorf(msg)
+				} else {
+					fmt.Printf("WARNING: " + msg)
+				}
+			} else {
+				if !asError {
+					fmt.Printf("PASSED: " + msg)
+				}
+			}
+		}
+	}
+}
+
 func aconfigUpdateAndroidBuildActions(ctx ModuleContext) {
 	mergedAconfigFiles := make(map[string]Paths)
+	mergedModeInfos := make(map[string]ModeInfo)
+
 	ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) {
+		if aconfig_dep, ok := OtherModuleProvider(ctx, module, CodegenInfoProvider); ok && len(aconfig_dep.ModeInfos) > 0 {
+			maps.Copy(mergedModeInfos, aconfig_dep.ModeInfos)
+		}
+
 		// If any of our dependencies have aconfig declarations (directly or propagated), then merge those and provide them.
 		if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok {
 			mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath)
@@ -105,6 +162,7 @@
 			for container, v := range dep.AconfigFiles {
 				mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...)
 			}
+			propagateModeInfos(ctx, module, mergedModeInfos, dep.ModeInfos)
 		}
 		if dep, ok := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); ok {
 			for container, v := range dep.AconfigFiles {
@@ -120,6 +178,7 @@
 
 		SetProvider(ctx, aconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{
 			AconfigFiles: mergedAconfigFiles,
+			ModeInfos:    mergedModeInfos,
 		})
 	}
 }
diff --git a/android/config.go b/android/config.go
index 936d1d3..951aed1 100644
--- a/android/config.go
+++ b/android/config.go
@@ -18,7 +18,6 @@
 // product variables necessary for soong_build's operation.
 
 import (
-	"android/soong/shared"
 	"encoding/json"
 	"fmt"
 	"os"
@@ -30,6 +29,8 @@
 	"sync"
 	"unicode"
 
+	"android/soong/shared"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
@@ -1932,6 +1933,10 @@
 	return c.config.productVariables.GenerateAidlNdkPlatformBackend
 }
 
+func (c *deviceConfig) AconfigContainerValidation() string {
+	return c.config.productVariables.AconfigContainerValidation
+}
+
 func (c *config) IgnorePrefer32OnDevice() bool {
 	return c.productVariables.IgnorePrefer32OnDevice
 }
diff --git a/android/deptag.go b/android/deptag.go
index a15443b..c7ba4d3 100644
--- a/android/deptag.go
+++ b/android/deptag.go
@@ -43,3 +43,15 @@
 	}
 	return false
 }
+
+type PropagateAconfigValidationDependencyTag interface {
+	PropagateAconfigValidation() bool
+}
+
+type AlwaysPropagateAconfigValidationDependencyTag struct{}
+
+func (p AlwaysPropagateAconfigValidationDependencyTag) PropagateAconfigValidation() bool {
+	return true
+}
+
+var _ PropagateAconfigValidationDependencyTag = AlwaysPropagateAconfigValidationDependencyTag{}
diff --git a/android/module.go b/android/module.go
index b615ff5..000476c 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,7 +15,6 @@
 package android
 
 import (
-	"android/soong/bazel"
 	"crypto/md5"
 	"encoding/hex"
 	"encoding/json"
@@ -27,6 +26,8 @@
 	"sort"
 	"strings"
 
+	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -2087,6 +2088,7 @@
 // caused by prebuilt_ prefix, or fully qualified module names.
 type sourceOrOutputDependencyTag struct {
 	blueprint.BaseDependencyTag
+	AlwaysPropagateAconfigValidationDependencyTag
 
 	// The name of the module.
 	moduleName string
diff --git a/android/variable.go b/android/variable.go
index be3c80d..73f5bfd 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -500,6 +500,8 @@
 	HiddenapiExportableStubs *bool `json:",omitempty"`
 
 	ExportRuntimeApis *bool `json:",omitempty"`
+
+	AconfigContainerValidation string `json:",omitempty"`
 }
 
 type PartitionQualifiedVariablesType struct {