summaryrefslogtreecommitdiff
path: root/android/container.go
diff options
context:
space:
mode:
Diffstat (limited to 'android/container.go')
-rw-r--r--android/container.go233
1 files changed, 233 insertions, 0 deletions
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)
+ }
+}