blob: c4fdd9c91ee4cacd8b536a421ee6e58361f2cf40 [file] [log] [blame]
// 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)
}
}