// 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) } }