diff --git a/android/Android.bp b/android/Android.bp index ce8c9b0b3..3b17be446 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -41,6 +41,7 @@ bootstrap_go_package { "build_prop.go", "buildinfo_prop.go", "config.go", + "container.go", "test_config.go", "configurable_properties.go", "configured_jars.go", diff --git a/android/container.go b/android/container.go new file mode 100644 index 000000000..c4fdd9c91 --- /dev/null +++ b/android/container.go @@ -0,0 +1,233 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import ( + "reflect" + "slices" + + "github.com/google/blueprint" +) + +type StubsAvailableModule interface { + IsStubsModule() bool +} + +// Returns true if the dependency module is a stubs module +var depIsStubsModule = func(_ ModuleContext, _, dep Module) bool { + if stubsModule, ok := dep.(StubsAvailableModule); ok { + return stubsModule.IsStubsModule() + } + return false +} + +// 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 +) + +// Functions cannot be used as a value passed in providers, because functions are not +// hashable. As a workaround, the exceptionHandleFunc enum values are passed using providers, +// and the corresponding functions are called from this map. +var exceptionHandleFunctionsTable = map[exceptionHandleFuncLabel]func(ModuleContext, Module, Module) bool{ + checkStubs: depIsStubsModule, +} + +type InstallableModule interface { + EnforceApiContainerChecks() bool +} + +type restriction struct { + // container of the dependency + dependency *container + + // Error message to be emitted to the user when the dependency meets this restriction + errorMessage string + + // List of labels of allowed exception functions that allows bypassing this restriction. + // If any of the functions mapped to each labels returns true, this dependency would be + // considered allowed and an error will not be thrown. + allowedExceptions []exceptionHandleFuncLabel +} +type container struct { + // The name of the container i.e. partition, api domain + name string + + // Map of dependency restricted containers. + restricted []restriction +} + +var ( + VendorContainer = &container{ + name: VendorVariation, + restricted: nil, + } + SystemContainer = &container{ + name: "system", + restricted: []restriction{ + { + dependency: VendorContainer, + errorMessage: "Module belonging to the system partition other than HALs is " + + "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{}, + }, + }, + } + ProductContainer = &container{ + name: ProductVariation, + restricted: []restriction{ + { + dependency: VendorContainer, + 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{}, + }, + }, + } + ApexContainer = initializeApexContainer() + CtsContainer = &container{ + 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}, + }, + }, + } +) + +func initializeApexContainer() *container { + apexContainer := &container{ + name: "apex", + restricted: []restriction{ + { + dependency: SystemContainer, + errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " + + "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}, + }, + }, + } + + apexContainer.restricted = append(apexContainer.restricted, restriction{ + dependency: apexContainer, + errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " + + "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}, + }) + + return apexContainer +} + +type ContainersInfo struct { + belongingContainers []*container + + belongingApexes []ApexInfo +} + +func (c *ContainersInfo) BelongingContainers() []*container { + return c.belongingContainers +} + +var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]() + +// Determines if the module can be installed in the system partition or not. +// Logic is identical to that of modulePartition(...) defined in paths.go +func installInSystemPartition(ctx ModuleContext) bool { + module := ctx.Module() + return !module.InstallInTestcases() && + !module.InstallInData() && + !module.InstallInRamdisk() && + !module.InstallInVendorRamdisk() && + !module.InstallInDebugRamdisk() && + !module.InstallInRecovery() && + !module.InstallInVendor() && + !module.InstallInOdm() && + !module.InstallInProduct() && + determineModuleKind(module.base(), ctx.blueprintBaseModuleContext()) == platformModule +} + +func generateContainerInfo(ctx ModuleContext) ContainersInfo { + inSystem := installInSystemPartition(ctx) + inProduct := ctx.Module().InstallInProduct() + inVendor := ctx.Module().InstallInVendor() + inCts := false + inApex := false + + if m, ok := ctx.Module().(ImageInterface); ok { + inProduct = inProduct || m.ProductVariantNeeded(ctx) + inVendor = inVendor || m.VendorVariantNeeded(ctx) + } + + props := ctx.Module().GetProperties() + for _, prop := range props { + val := reflect.ValueOf(prop).Elem() + if val.Kind() == reflect.Struct { + testSuites := val.FieldByName("Test_suites") + if testSuites.IsValid() && testSuites.Kind() == reflect.Slice && slices.Contains(testSuites.Interface().([]string), "cts") { + inCts = true + } + } + } + + var belongingApexes []ApexInfo + if apexInfo, ok := ModuleProvider(ctx, AllApexInfoProvider); ok { + belongingApexes = apexInfo.ApexInfos + inApex = true + } + + containers := []*container{} + if inSystem { + containers = append(containers, SystemContainer) + } + if inProduct { + containers = append(containers, ProductContainer) + } + if inVendor { + containers = append(containers, VendorContainer) + } + if inCts { + containers = append(containers, CtsContainer) + } + if inApex { + containers = append(containers, ApexContainer) + } + + return ContainersInfo{ + belongingContainers: containers, + belongingApexes: belongingApexes, + } +} + +func setContainerInfo(ctx ModuleContext) { + if _, ok := ctx.Module().(InstallableModule); ok { + containersInfo := generateContainerInfo(ctx) + SetProvider(ctx, ContainersInfoProvider, containersInfo) + } +} diff --git a/android/image.go b/android/image.go index c278dcdf9..0f0310701 100644 --- a/android/image.go +++ b/android/image.go @@ -22,7 +22,7 @@ type ImageInterface interface { // VendorVariantNeeded should return true if the module needs a vendor variant (installed on the vendor image). VendorVariantNeeded(ctx BaseModuleContext) bool - // ProductVariantNeeded should return true if the module needs a product variant (unstalled on the product image). + // ProductVariantNeeded should return true if the module needs a product variant (installed on the product image). ProductVariantNeeded(ctx BaseModuleContext) bool // CoreVariantNeeded should return true if the module needs a core variant (installed on the system image). diff --git a/android/module.go b/android/module.go index 2dc63d6e5..f4a188f15 100644 --- a/android/module.go +++ b/android/module.go @@ -1801,6 +1801,8 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) variables: make(map[string]string), } + setContainerInfo(ctx) + m.licenseMetadataFile = PathForModuleOut(ctx, "meta_lic") dependencyInstallFiles, dependencyPackagingSpecs := m.computeInstallDeps(ctx)