jacocoagent is implicitly added as a dependency in coverage builds, and is not installed on the device. Test: m nothing Bug: 338660802 Change-Id: Icf14ce5f9b0819a3c5fbe74cf1674719859ad5c6
439 lines
15 KiB
Go
439 lines
15 KiB
Go
// 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"
|
|
)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Start of the definitions of exception functions and the lookup table.
|
|
//
|
|
// Functions cannot be used as a value passed in providers, because functions are not
|
|
// hashable. As a workaround, the [exceptionHandleFuncLabel] enum values are passed using providers,
|
|
// and the corresponding functions are called from [exceptionHandleFunctionsTable] map.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
type exceptionHandleFunc func(ModuleContext, Module, Module) bool
|
|
|
|
type StubsAvailableModule interface {
|
|
IsStubsModule() bool
|
|
}
|
|
|
|
// Returns true if the dependency module is a stubs module
|
|
var depIsStubsModule exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
|
|
if stubsModule, ok := dep.(StubsAvailableModule); ok {
|
|
return stubsModule.IsStubsModule()
|
|
}
|
|
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",
|
|
|
|
// jacocoagent is implicitly added as a dependency in coverage builds, and is not installed
|
|
// on the device.
|
|
"jacocoagent",
|
|
}
|
|
|
|
// 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,
|
|
checkApexModule: depIsApexModule,
|
|
checkInCommonApexes: belongsToCommonApexes,
|
|
checkApexIsNonUpdatable: belongsToNonUpdatableApex,
|
|
checkNotDynamicDepTag: depIsNotDynamicDepTag,
|
|
checkNotStaticOrDynamicDepTag: depIsNotStaticOrDynamicDepTag,
|
|
checkGlobalAllowlistedDep: depIsGloballyAllowlisted,
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Start of the definitions of container determination functions.
|
|
//
|
|
// Similar to the above section, below defines the functions used to determine
|
|
// the container of each modules.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
type containerBoundaryFunc func(mctx ModuleContext) bool
|
|
|
|
var vendorContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
|
|
m, ok := mctx.Module().(ImageInterface)
|
|
return mctx.Module().InstallInVendor() || (ok && m.VendorVariantNeeded(mctx))
|
|
}
|
|
|
|
var systemContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
|
|
module := mctx.Module()
|
|
|
|
return !module.InstallInTestcases() &&
|
|
!module.InstallInData() &&
|
|
!module.InstallInRamdisk() &&
|
|
!module.InstallInVendorRamdisk() &&
|
|
!module.InstallInDebugRamdisk() &&
|
|
!module.InstallInRecovery() &&
|
|
!module.InstallInVendor() &&
|
|
!module.InstallInOdm() &&
|
|
!module.InstallInProduct() &&
|
|
determineModuleKind(module.base(), mctx.blueprintBaseModuleContext()) == platformModule
|
|
}
|
|
|
|
var productContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
|
|
m, ok := mctx.Module().(ImageInterface)
|
|
return mctx.Module().InstallInProduct() || (ok && m.ProductVariantNeeded(mctx))
|
|
}
|
|
|
|
var apexContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
|
|
_, ok := ModuleProvider(mctx, AllApexInfoProvider)
|
|
return ok
|
|
}
|
|
|
|
var ctsContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
|
|
props := mctx.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") {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
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,
|
|
UnstableContainer: unstableContainerBoundaryFunc,
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// End of the definitions of container determination functions.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
type InstallableModule interface {
|
|
ContainersInfo() ContainersInfo
|
|
StaticDependencyTags() []blueprint.DependencyTag
|
|
DynamicDependencyTags() []blueprint.DependencyTag
|
|
}
|
|
|
|
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{
|
|
checkStubs,
|
|
checkNotDynamicDepTag,
|
|
checkGlobalAllowlistedDep,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
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{
|
|
checkStubs,
|
|
checkNotDynamicDepTag,
|
|
checkGlobalAllowlistedDep,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ApexContainer = initializeApexContainer()
|
|
|
|
CtsContainer = &container{
|
|
name: "cts",
|
|
restricted: []restriction{
|
|
{
|
|
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,
|
|
}
|
|
)
|
|
|
|
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,
|
|
checkApexModule,
|
|
checkInCommonApexes,
|
|
checkApexIsNonUpdatable,
|
|
checkNotStaticOrDynamicDepTag,
|
|
checkGlobalAllowlistedDep,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
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,
|
|
checkInCommonApexes,
|
|
checkNotStaticOrDynamicDepTag,
|
|
checkGlobalAllowlistedDep,
|
|
},
|
|
})
|
|
|
|
return apexContainer
|
|
}
|
|
|
|
type ContainersInfo struct {
|
|
belongingContainers []*container
|
|
|
|
belongingApexes []ApexInfo
|
|
}
|
|
|
|
func (c *ContainersInfo) BelongingContainers() []*container {
|
|
return c.belongingContainers
|
|
}
|
|
|
|
func (c *ContainersInfo) ApexNames() (ret []string) {
|
|
for _, apex := range c.belongingApexes {
|
|
ret = append(ret, apex.InApexModules...)
|
|
}
|
|
slices.Sort(ret)
|
|
return ret
|
|
}
|
|
|
|
// Returns true if any of the apex the module belongs to is updatable.
|
|
func (c *ContainersInfo) UpdatableApex() bool {
|
|
for _, apex := range c.belongingApexes {
|
|
if apex.Updatable {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]()
|
|
|
|
func generateContainerInfo(ctx ModuleContext) ContainersInfo {
|
|
var containers []*container
|
|
|
|
for _, cnt := range allContainers {
|
|
if containerBoundaryFunctionsTable[cnt](ctx) {
|
|
containers = append(containers, cnt)
|
|
}
|
|
}
|
|
|
|
var belongingApexes []ApexInfo
|
|
if apexInfo, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
|
|
belongingApexes = apexInfo.ApexInfos
|
|
}
|
|
|
|
return ContainersInfo{
|
|
belongingContainers: containers,
|
|
belongingApexes: belongingApexes,
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|