diff --git a/android/container.go b/android/container.go index 05897dd6c..10aff4d2b 100644 --- a/android/container.go +++ b/android/container.go @@ -43,16 +43,86 @@ var depIsStubsModule exceptionHandleFunc = func(_ ModuleContext, _, dep Module) return false } +// Returns true if the dependency module belongs to any of the apexes. +var depIsApexModule exceptionHandleFunc = func(mctx ModuleContext, _, dep Module) bool { + depContainersInfo, _ := getContainerModuleInfo(mctx, dep) + return InList(ApexContainer, depContainersInfo.belongingContainers) +} + +// Returns true if the module and the dependent module belongs to common apexes. +var belongsToCommonApexes exceptionHandleFunc = func(mctx ModuleContext, m, dep Module) bool { + mContainersInfo, _ := getContainerModuleInfo(mctx, m) + depContainersInfo, _ := getContainerModuleInfo(mctx, dep) + + return HasIntersection(mContainersInfo.ApexNames(), depContainersInfo.ApexNames()) +} + +// Returns true when all apexes that the module belongs to are non updatable. +// For an apex module to be allowed to depend on a non-apex partition module, +// all apexes that the module belong to must be non updatable. +var belongsToNonUpdatableApex exceptionHandleFunc = func(mctx ModuleContext, m, _ Module) bool { + mContainersInfo, _ := getContainerModuleInfo(mctx, m) + + return !mContainersInfo.UpdatableApex() +} + +// Returns true if the dependency is added via dependency tags that are not used to tag dynamic +// dependency tags. +var depIsNotDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool { + mInstallable, _ := m.(InstallableModule) + depTag := ctx.OtherModuleDependencyTag(dep) + return !InList(depTag, mInstallable.DynamicDependencyTags()) +} + +// Returns true if the dependency is added via dependency tags that are not used to tag static +// or dynamic dependency tags. These dependencies do not affect the module in compile time or in +// runtime, thus are not significant enough to raise an error. +var depIsNotStaticOrDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool { + mInstallable, _ := m.(InstallableModule) + depTag := ctx.OtherModuleDependencyTag(dep) + return !InList(depTag, append(mInstallable.StaticDependencyTags(), mInstallable.DynamicDependencyTags()...)) +} + +var globallyAllowlistedDependencies = []string{ + // Modules that provide annotations used within the platform and apexes. + "aconfig-annotations-lib", + "framework-annotations-lib", + "unsupportedappusage", + + // framework-res provides core resources essential for building apps and system UI. + // This module is implicitly added as a dependency for java modules even when the + // dependency specifies sdk_version. + "framework-res", +} + +// Returns true when the dependency is globally allowlisted for inter-container dependency +var depIsGloballyAllowlisted exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool { + return InList(dep.Name(), globallyAllowlistedDependencies) +} + // Labels of exception functions, which are used to determine special dependencies that allow // otherwise restricted inter-container dependencies type exceptionHandleFuncLabel int const ( checkStubs exceptionHandleFuncLabel = iota + checkApexModule + checkInCommonApexes + checkApexIsNonUpdatable + checkNotDynamicDepTag + checkNotStaticOrDynamicDepTag + checkGlobalAllowlistedDep ) +// Map of [exceptionHandleFuncLabel] to the [exceptionHandleFunc] var exceptionHandleFunctionsTable = map[exceptionHandleFuncLabel]exceptionHandleFunc{ - checkStubs: depIsStubsModule, + checkStubs: depIsStubsModule, + checkApexModule: depIsApexModule, + checkInCommonApexes: belongsToCommonApexes, + checkApexIsNonUpdatable: belongsToNonUpdatableApex, + checkNotDynamicDepTag: depIsNotDynamicDepTag, + checkNotStaticOrDynamicDepTag: depIsNotStaticOrDynamicDepTag, + checkGlobalAllowlistedDep: depIsGloballyAllowlisted, } // ---------------------------------------------------------------------------- @@ -108,13 +178,40 @@ var ctsContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bo 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, } // ---------------------------------------------------------------------------- @@ -122,7 +219,9 @@ var containerBoundaryFunctionsTable = map[*container]containerBoundaryFunc{ // ---------------------------------------------------------------------------- type InstallableModule interface { - EnforceApiContainerChecks() bool + ContainersInfo() ContainersInfo + StaticDependencyTags() []blueprint.DependencyTag + DynamicDependencyTags() []blueprint.DependencyTag } type restriction struct { @@ -160,7 +259,11 @@ var ( "not allowed to depend on the vendor partition module, in order to support " + "independent development/update cycles and to support the Generic System " + "Image. Try depending on HALs, VNDK or AIDL instead.", - allowedExceptions: []exceptionHandleFuncLabel{}, + allowedExceptions: []exceptionHandleFuncLabel{ + checkStubs, + checkNotDynamicDepTag, + checkGlobalAllowlistedDep, + }, }, }, } @@ -173,7 +276,11 @@ var ( errorMessage: "Module belonging to the product partition is not allowed to " + "depend on the vendor partition module, as this may lead to security " + "vulnerabilities. Try depending on the HALs or utilize AIDL instead.", - allowedExceptions: []exceptionHandleFuncLabel{}, + allowedExceptions: []exceptionHandleFuncLabel{ + checkStubs, + checkNotDynamicDepTag, + checkGlobalAllowlistedDep, + }, }, }, } @@ -184,22 +291,34 @@ var ( 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.", - allowedExceptions: []exceptionHandleFuncLabel{checkStubs}, + 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, + checkGlobalAllowlistedDep, + }, }, }, } + // Container signifying that the module contains unstable platform private APIs + UnstableContainer = &container{ + name: "unstable", + restricted: nil, + } + allContainers = []*container{ VendorContainer, SystemContainer, ProductContainer, ApexContainer, CtsContainer, + UnstableContainer, } ) @@ -213,7 +332,14 @@ func initializeApexContainer() *container { "modules belonging to the system partition. Either statically depend on the " + "module or convert the depending module to java_sdk_library and depend on " + "the stubs.", - allowedExceptions: []exceptionHandleFuncLabel{checkStubs}, + allowedExceptions: []exceptionHandleFuncLabel{ + checkStubs, + checkApexModule, + checkInCommonApexes, + checkApexIsNonUpdatable, + checkNotStaticOrDynamicDepTag, + checkGlobalAllowlistedDep, + }, }, }, } @@ -224,7 +350,12 @@ func initializeApexContainer() *container { "modules belonging to other Apex(es). Either include the depending " + "module in the Apex or convert the depending module to java_sdk_library " + "and depend on its stubs.", - allowedExceptions: []exceptionHandleFuncLabel{checkStubs}, + allowedExceptions: []exceptionHandleFuncLabel{ + checkStubs, + checkInCommonApexes, + checkNotStaticOrDynamicDepTag, + checkGlobalAllowlistedDep, + }, }) return apexContainer @@ -280,9 +411,24 @@ func generateContainerInfo(ctx ModuleContext) ContainersInfo { } } +func getContainerModuleInfo(ctx ModuleContext, module Module) (ContainersInfo, bool) { + if ctx.Module() == module { + return module.ContainersInfo(), true + } + + return OtherModuleProvider(ctx, module, ContainersInfoProvider) +} + 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 SetProvider(ctx, ContainersInfoProvider, containersInfo) } } diff --git a/android/module.go b/android/module.go index b625be46e..664ac5c71 100644 --- a/android/module.go +++ b/android/module.go @@ -118,6 +118,9 @@ type Module interface { TransitivePackagingSpecs() []PackagingSpec ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator + + // Get the information about the containers this module belongs to. + ContainersInfo() ContainersInfo } // Qualified id for a module @@ -885,6 +888,10 @@ type ModuleBase struct { // complianceMetadataInfo is for different module types to dump metadata. // See android.ModuleContext interface. complianceMetadataInfo *ComplianceMetadataInfo + + // containersInfo stores the information about the containers and the information of the + // apexes the module belongs to. + containersInfo ContainersInfo } func (m *ModuleBase) AddJSONData(d *map[string]interface{}) { @@ -2091,6 +2098,10 @@ func (m *ModuleBase) moduleInfoVariant(ctx ModuleContext) string { return variant } +func (m *ModuleBase) ContainersInfo() ContainersInfo { + return m.containersInfo +} + // Check the supplied dist structure to make sure that it is valid. // // property - the base property, e.g. dist or dists[1], which is combined with the diff --git a/java/base.go b/java/base.go index 359f2513d..a03b4ae74 100644 --- a/java/base.go +++ b/java/base.go @@ -571,8 +571,14 @@ type Module struct { var _ android.InstallableModule = (*Module)(nil) // To satisfy the InstallableModule interface -func (j *Module) EnforceApiContainerChecks() bool { - return true +func (j *Module) StaticDependencyTags() []blueprint.DependencyTag { + return []blueprint.DependencyTag{staticLibTag} +} + +// To satisfy the InstallableModule interface +func (j *Module) DynamicDependencyTags() []blueprint.DependencyTag { + return []blueprint.DependencyTag{libTag, sdkLibTag, bootClasspathTag, systemModulesTag, + instrumentationForTag, java9LibTag} } // Overrides android.ModuleBase.InstallInProduct() diff --git a/java/container_test.go b/java/container_test.go index 344185553..25cfa4c7e 100644 --- a/java/container_test.go +++ b/java/container_test.go @@ -65,6 +65,18 @@ func TestJavaContainersModuleProperties(t *testing.T) { "general-tests", ], } + java_library { + name: "bar", + static_libs: [ + "framework-minus-apex", + ], + } + java_library { + name: "baz", + static_libs: [ + "bar", + ], + } `) testcases := []struct { @@ -73,6 +85,7 @@ func TestJavaContainersModuleProperties(t *testing.T) { isVendorContainer bool isProductContainer bool isCts bool + isUnstable bool }{ { moduleName: "foo", @@ -80,6 +93,7 @@ func TestJavaContainersModuleProperties(t *testing.T) { isVendorContainer: false, isProductContainer: false, isCts: false, + isUnstable: false, }, { moduleName: "foo_vendor", @@ -87,6 +101,7 @@ func TestJavaContainersModuleProperties(t *testing.T) { isVendorContainer: true, isProductContainer: false, isCts: false, + isUnstable: false, }, { moduleName: "foo_soc_specific", @@ -94,6 +109,7 @@ func TestJavaContainersModuleProperties(t *testing.T) { isVendorContainer: true, isProductContainer: false, isCts: false, + isUnstable: false, }, { moduleName: "foo_product_specific", @@ -101,6 +117,7 @@ func TestJavaContainersModuleProperties(t *testing.T) { isVendorContainer: false, isProductContainer: true, isCts: false, + isUnstable: false, }, { moduleName: "foo_cts_test", @@ -108,6 +125,7 @@ func TestJavaContainersModuleProperties(t *testing.T) { isVendorContainer: false, isProductContainer: false, isCts: true, + isUnstable: false, }, { moduleName: "foo_non_cts_test", @@ -115,6 +133,23 @@ func TestJavaContainersModuleProperties(t *testing.T) { isVendorContainer: false, isProductContainer: false, isCts: false, + isUnstable: false, + }, + { + moduleName: "bar", + isSystemContainer: true, + isVendorContainer: false, + isProductContainer: false, + isCts: false, + isUnstable: true, + }, + { + moduleName: "baz", + isSystemContainer: true, + isVendorContainer: false, + isProductContainer: false, + isCts: false, + isUnstable: true, }, } @@ -125,5 +160,7 @@ func TestJavaContainersModuleProperties(t *testing.T) { checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers)) checkContainerMatch(t, c.moduleName, "vendor", c.isVendorContainer, android.InList(android.VendorContainer, belongingContainers)) checkContainerMatch(t, c.moduleName, "product", c.isProductContainer, android.InList(android.ProductContainer, belongingContainers)) + checkContainerMatch(t, c.moduleName, "cts", c.isCts, android.InList(android.CtsContainer, belongingContainers)) + checkContainerMatch(t, c.moduleName, "unstable", c.isUnstable, android.InList(android.UnstableContainer, belongingContainers)) } } diff --git a/java/testing.go b/java/testing.go index e1bf5376e..0e85022ce 100644 --- a/java/testing.go +++ b/java/testing.go @@ -542,6 +542,16 @@ func gatherRequiredDepsForTest() string { }, compile_dex: true, } + java_library { + name: "framework-minus-apex", + srcs: ["a.java"], + sdk_version: "none", + system_modules: "stable-core-platform-api-stubs-system-modules", + aidl: { + export_include_dirs: ["framework/aidl"], + }, + compile_dex: true, + } android_app { name: "framework-res",