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