blob: 63fb362ad9a7d11c8b2b2f8d4ea327c0f522c4f8 [file] [log] [blame]
Jihoon Kangc3d4e112024-06-24 22:16:27 +00001// Copyright 2024 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
Jihoon Kang2a884912024-07-01 17:04:46 +000018 "fmt"
Jihoon Kangc3d4e112024-06-24 22:16:27 +000019 "reflect"
20 "slices"
Jihoon Kang2a884912024-07-01 17:04:46 +000021 "strings"
Jihoon Kangc3d4e112024-06-24 22:16:27 +000022
23 "github.com/google/blueprint"
24)
25
Jihoon Kang17a61d72024-08-12 22:26:52 +000026// ----------------------------------------------------------------------------
27// Start of the definitions of exception functions and the lookup table.
28//
29// Functions cannot be used as a value passed in providers, because functions are not
30// hashable. As a workaround, the [exceptionHandleFuncLabel] enum values are passed using providers,
31// and the corresponding functions are called from [exceptionHandleFunctionsTable] map.
32// ----------------------------------------------------------------------------
33
34type exceptionHandleFunc func(ModuleContext, Module, Module) bool
35
Jihoon Kangc3d4e112024-06-24 22:16:27 +000036type StubsAvailableModule interface {
37 IsStubsModule() bool
38}
39
40// Returns true if the dependency module is a stubs module
Jihoon Kang17a61d72024-08-12 22:26:52 +000041var depIsStubsModule exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
Jihoon Kangc3d4e112024-06-24 22:16:27 +000042 if stubsModule, ok := dep.(StubsAvailableModule); ok {
43 return stubsModule.IsStubsModule()
44 }
45 return false
46}
47
Jihoon Kang224ea082024-08-12 22:38:16 +000048// Returns true if the dependency module belongs to any of the apexes.
49var depIsApexModule exceptionHandleFunc = func(mctx ModuleContext, _, dep Module) bool {
50 depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
51 return InList(ApexContainer, depContainersInfo.belongingContainers)
52}
53
54// Returns true if the module and the dependent module belongs to common apexes.
55var belongsToCommonApexes exceptionHandleFunc = func(mctx ModuleContext, m, dep Module) bool {
56 mContainersInfo, _ := getContainerModuleInfo(mctx, m)
57 depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
58
59 return HasIntersection(mContainersInfo.ApexNames(), depContainersInfo.ApexNames())
60}
61
62// Returns true when all apexes that the module belongs to are non updatable.
63// For an apex module to be allowed to depend on a non-apex partition module,
64// all apexes that the module belong to must be non updatable.
65var belongsToNonUpdatableApex exceptionHandleFunc = func(mctx ModuleContext, m, _ Module) bool {
66 mContainersInfo, _ := getContainerModuleInfo(mctx, m)
67
68 return !mContainersInfo.UpdatableApex()
69}
70
71// Returns true if the dependency is added via dependency tags that are not used to tag dynamic
72// dependency tags.
73var depIsNotDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
74 mInstallable, _ := m.(InstallableModule)
75 depTag := ctx.OtherModuleDependencyTag(dep)
76 return !InList(depTag, mInstallable.DynamicDependencyTags())
77}
78
79// Returns true if the dependency is added via dependency tags that are not used to tag static
80// or dynamic dependency tags. These dependencies do not affect the module in compile time or in
81// runtime, thus are not significant enough to raise an error.
82var depIsNotStaticOrDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
83 mInstallable, _ := m.(InstallableModule)
84 depTag := ctx.OtherModuleDependencyTag(dep)
85 return !InList(depTag, append(mInstallable.StaticDependencyTags(), mInstallable.DynamicDependencyTags()...))
86}
87
88var globallyAllowlistedDependencies = []string{
89 // Modules that provide annotations used within the platform and apexes.
90 "aconfig-annotations-lib",
91 "framework-annotations-lib",
92 "unsupportedappusage",
93
94 // framework-res provides core resources essential for building apps and system UI.
95 // This module is implicitly added as a dependency for java modules even when the
96 // dependency specifies sdk_version.
97 "framework-res",
Jihoon Kang601939d2024-08-27 17:27:52 +000098
99 // jacocoagent is implicitly added as a dependency in coverage builds, and is not installed
100 // on the device.
101 "jacocoagent",
Jihoon Kang224ea082024-08-12 22:38:16 +0000102}
103
104// Returns true when the dependency is globally allowlisted for inter-container dependency
105var depIsGloballyAllowlisted exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
106 return InList(dep.Name(), globallyAllowlistedDependencies)
107}
108
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000109// Labels of exception functions, which are used to determine special dependencies that allow
110// otherwise restricted inter-container dependencies
111type exceptionHandleFuncLabel int
112
113const (
114 checkStubs exceptionHandleFuncLabel = iota
Jihoon Kang224ea082024-08-12 22:38:16 +0000115 checkApexModule
116 checkInCommonApexes
117 checkApexIsNonUpdatable
118 checkNotDynamicDepTag
119 checkNotStaticOrDynamicDepTag
120 checkGlobalAllowlistedDep
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000121)
122
Jihoon Kang224ea082024-08-12 22:38:16 +0000123// Map of [exceptionHandleFuncLabel] to the [exceptionHandleFunc]
Jihoon Kang17a61d72024-08-12 22:26:52 +0000124var exceptionHandleFunctionsTable = map[exceptionHandleFuncLabel]exceptionHandleFunc{
Jihoon Kang224ea082024-08-12 22:38:16 +0000125 checkStubs: depIsStubsModule,
126 checkApexModule: depIsApexModule,
127 checkInCommonApexes: belongsToCommonApexes,
128 checkApexIsNonUpdatable: belongsToNonUpdatableApex,
129 checkNotDynamicDepTag: depIsNotDynamicDepTag,
130 checkNotStaticOrDynamicDepTag: depIsNotStaticOrDynamicDepTag,
131 checkGlobalAllowlistedDep: depIsGloballyAllowlisted,
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000132}
133
Jihoon Kang17a61d72024-08-12 22:26:52 +0000134// ----------------------------------------------------------------------------
135// Start of the definitions of container determination functions.
136//
137// Similar to the above section, below defines the functions used to determine
138// the container of each modules.
139// ----------------------------------------------------------------------------
140
141type containerBoundaryFunc func(mctx ModuleContext) bool
142
143var vendorContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
144 m, ok := mctx.Module().(ImageInterface)
145 return mctx.Module().InstallInVendor() || (ok && m.VendorVariantNeeded(mctx))
146}
147
148var systemContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
149 module := mctx.Module()
150
151 return !module.InstallInTestcases() &&
152 !module.InstallInData() &&
153 !module.InstallInRamdisk() &&
154 !module.InstallInVendorRamdisk() &&
155 !module.InstallInDebugRamdisk() &&
156 !module.InstallInRecovery() &&
157 !module.InstallInVendor() &&
158 !module.InstallInOdm() &&
159 !module.InstallInProduct() &&
160 determineModuleKind(module.base(), mctx.blueprintBaseModuleContext()) == platformModule
161}
162
163var productContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
164 m, ok := mctx.Module().(ImageInterface)
165 return mctx.Module().InstallInProduct() || (ok && m.ProductVariantNeeded(mctx))
166}
167
168var apexContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
169 _, ok := ModuleProvider(mctx, AllApexInfoProvider)
170 return ok
171}
172
173var ctsContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
174 props := mctx.Module().GetProperties()
175 for _, prop := range props {
176 val := reflect.ValueOf(prop).Elem()
177 if val.Kind() == reflect.Struct {
178 testSuites := val.FieldByName("Test_suites")
179 if testSuites.IsValid() && testSuites.Kind() == reflect.Slice && slices.Contains(testSuites.Interface().([]string), "cts") {
180 return true
181 }
182 }
183 }
184 return false
185}
186
Jihoon Kang0f3b1a72024-08-12 22:47:01 +0000187type unstableInfo struct {
188 // Determines if the module contains the private APIs of the platform.
189 ContainsPlatformPrivateApis bool
190}
191
192var unstableInfoProvider = blueprint.NewProvider[unstableInfo]()
193
194func determineUnstableModule(mctx ModuleContext) bool {
195 module := mctx.Module()
196 unstableModule := module.Name() == "framework-minus-apex"
197 if installable, ok := module.(InstallableModule); ok {
198 for _, staticDepTag := range installable.StaticDependencyTags() {
199 mctx.VisitDirectDepsWithTag(staticDepTag, func(dep Module) {
200 if unstableInfo, ok := OtherModuleProvider(mctx, dep, unstableInfoProvider); ok {
201 unstableModule = unstableModule || unstableInfo.ContainsPlatformPrivateApis
202 }
203 })
204 }
205 }
206 return unstableModule
207}
208
209var unstableContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
210 return determineUnstableModule(mctx)
211}
212
Jihoon Kang17a61d72024-08-12 22:26:52 +0000213// Map of [*container] to the [containerBoundaryFunc]
214var containerBoundaryFunctionsTable = map[*container]containerBoundaryFunc{
Jihoon Kang0f3b1a72024-08-12 22:47:01 +0000215 VendorContainer: vendorContainerBoundaryFunc,
216 SystemContainer: systemContainerBoundaryFunc,
217 ProductContainer: productContainerBoundaryFunc,
218 ApexContainer: apexContainerBoundaryFunc,
219 CtsContainer: ctsContainerBoundaryFunc,
220 UnstableContainer: unstableContainerBoundaryFunc,
Jihoon Kang17a61d72024-08-12 22:26:52 +0000221}
222
223// ----------------------------------------------------------------------------
224// End of the definitions of container determination functions.
225// ----------------------------------------------------------------------------
226
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000227type InstallableModule interface {
Jihoon Kang224ea082024-08-12 22:38:16 +0000228 ContainersInfo() ContainersInfo
229 StaticDependencyTags() []blueprint.DependencyTag
230 DynamicDependencyTags() []blueprint.DependencyTag
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000231}
232
233type restriction struct {
234 // container of the dependency
235 dependency *container
236
237 // Error message to be emitted to the user when the dependency meets this restriction
238 errorMessage string
239
240 // List of labels of allowed exception functions that allows bypassing this restriction.
241 // If any of the functions mapped to each labels returns true, this dependency would be
242 // considered allowed and an error will not be thrown.
243 allowedExceptions []exceptionHandleFuncLabel
244}
245type container struct {
246 // The name of the container i.e. partition, api domain
247 name string
248
249 // Map of dependency restricted containers.
250 restricted []restriction
251}
252
253var (
254 VendorContainer = &container{
255 name: VendorVariation,
256 restricted: nil,
257 }
Jihoon Kang17a61d72024-08-12 22:26:52 +0000258
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000259 SystemContainer = &container{
260 name: "system",
261 restricted: []restriction{
262 {
263 dependency: VendorContainer,
264 errorMessage: "Module belonging to the system partition other than HALs is " +
265 "not allowed to depend on the vendor partition module, in order to support " +
266 "independent development/update cycles and to support the Generic System " +
267 "Image. Try depending on HALs, VNDK or AIDL instead.",
Jihoon Kang224ea082024-08-12 22:38:16 +0000268 allowedExceptions: []exceptionHandleFuncLabel{
269 checkStubs,
270 checkNotDynamicDepTag,
271 checkGlobalAllowlistedDep,
272 },
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000273 },
274 },
275 }
Jihoon Kang17a61d72024-08-12 22:26:52 +0000276
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000277 ProductContainer = &container{
278 name: ProductVariation,
279 restricted: []restriction{
280 {
281 dependency: VendorContainer,
282 errorMessage: "Module belonging to the product partition is not allowed to " +
283 "depend on the vendor partition module, as this may lead to security " +
284 "vulnerabilities. Try depending on the HALs or utilize AIDL instead.",
Jihoon Kang224ea082024-08-12 22:38:16 +0000285 allowedExceptions: []exceptionHandleFuncLabel{
286 checkStubs,
287 checkNotDynamicDepTag,
288 checkGlobalAllowlistedDep,
289 },
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000290 },
291 },
292 }
Jihoon Kang17a61d72024-08-12 22:26:52 +0000293
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000294 ApexContainer = initializeApexContainer()
Jihoon Kang17a61d72024-08-12 22:26:52 +0000295
296 CtsContainer = &container{
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000297 name: "cts",
298 restricted: []restriction{
299 {
Jihoon Kang0f3b1a72024-08-12 22:47:01 +0000300 dependency: UnstableContainer,
301 errorMessage: "CTS module should not depend on the modules that contain the " +
302 "platform implementation details, including \"framework\". Depending on these " +
303 "modules may lead to disclosure of implementation details and regression " +
304 "due to API changes across platform versions. Try depending on the stubs instead " +
305 "and ensure that the module sets an appropriate 'sdk_version'.",
Jihoon Kang224ea082024-08-12 22:38:16 +0000306 allowedExceptions: []exceptionHandleFuncLabel{
307 checkStubs,
308 checkNotStaticOrDynamicDepTag,
309 checkGlobalAllowlistedDep,
310 },
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000311 },
312 },
313 }
Jihoon Kang17a61d72024-08-12 22:26:52 +0000314
Jihoon Kang0f3b1a72024-08-12 22:47:01 +0000315 // Container signifying that the module contains unstable platform private APIs
316 UnstableContainer = &container{
317 name: "unstable",
318 restricted: nil,
319 }
320
Jihoon Kang17a61d72024-08-12 22:26:52 +0000321 allContainers = []*container{
322 VendorContainer,
323 SystemContainer,
324 ProductContainer,
325 ApexContainer,
326 CtsContainer,
Jihoon Kang0f3b1a72024-08-12 22:47:01 +0000327 UnstableContainer,
Jihoon Kang17a61d72024-08-12 22:26:52 +0000328 }
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000329)
330
331func initializeApexContainer() *container {
332 apexContainer := &container{
333 name: "apex",
334 restricted: []restriction{
335 {
336 dependency: SystemContainer,
337 errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " +
338 "modules belonging to the system partition. Either statically depend on the " +
339 "module or convert the depending module to java_sdk_library and depend on " +
340 "the stubs.",
Jihoon Kang224ea082024-08-12 22:38:16 +0000341 allowedExceptions: []exceptionHandleFuncLabel{
342 checkStubs,
343 checkApexModule,
344 checkInCommonApexes,
345 checkApexIsNonUpdatable,
346 checkNotStaticOrDynamicDepTag,
347 checkGlobalAllowlistedDep,
348 },
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000349 },
350 },
351 }
352
353 apexContainer.restricted = append(apexContainer.restricted, restriction{
354 dependency: apexContainer,
355 errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " +
356 "modules belonging to other Apex(es). Either include the depending " +
357 "module in the Apex or convert the depending module to java_sdk_library " +
358 "and depend on its stubs.",
Jihoon Kang224ea082024-08-12 22:38:16 +0000359 allowedExceptions: []exceptionHandleFuncLabel{
360 checkStubs,
361 checkInCommonApexes,
362 checkNotStaticOrDynamicDepTag,
363 checkGlobalAllowlistedDep,
364 },
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000365 })
366
367 return apexContainer
368}
369
370type ContainersInfo struct {
371 belongingContainers []*container
372
373 belongingApexes []ApexInfo
374}
375
376func (c *ContainersInfo) BelongingContainers() []*container {
377 return c.belongingContainers
378}
379
Jihoon Kang17a61d72024-08-12 22:26:52 +0000380func (c *ContainersInfo) ApexNames() (ret []string) {
381 for _, apex := range c.belongingApexes {
382 ret = append(ret, apex.InApexModules...)
383 }
384 slices.Sort(ret)
385 return ret
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000386}
387
Jihoon Kang17a61d72024-08-12 22:26:52 +0000388// Returns true if any of the apex the module belongs to is updatable.
389func (c *ContainersInfo) UpdatableApex() bool {
390 for _, apex := range c.belongingApexes {
391 if apex.Updatable {
392 return true
393 }
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000394 }
Jihoon Kang17a61d72024-08-12 22:26:52 +0000395 return false
396}
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000397
Jihoon Kang17a61d72024-08-12 22:26:52 +0000398var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]()
399
Jihoon Kang2a884912024-07-01 17:04:46 +0000400func satisfyAllowedExceptions(ctx ModuleContext, allowedExceptionLabels []exceptionHandleFuncLabel, m, dep Module) bool {
401 for _, label := range allowedExceptionLabels {
402 if exceptionHandleFunctionsTable[label](ctx, m, dep) {
403 return true
404 }
405 }
406 return false
407}
408
409func (c *ContainersInfo) GetViolations(mctx ModuleContext, m, dep Module, depInfo ContainersInfo) []string {
410 var violations []string
411
412 // Any containers that the module belongs to but the dependency does not belong to must be examined.
413 _, containersUniqueToModule, _ := ListSetDifference(c.belongingContainers, depInfo.belongingContainers)
414
415 // Apex container should be examined even if both the module and the dependency belong to
416 // the apex container to check that the two modules belong to the same apex.
417 if InList(ApexContainer, c.belongingContainers) && !InList(ApexContainer, containersUniqueToModule) {
418 containersUniqueToModule = append(containersUniqueToModule, ApexContainer)
419 }
420
421 for _, containerUniqueToModule := range containersUniqueToModule {
422 for _, restriction := range containerUniqueToModule.restricted {
423 if InList(restriction.dependency, depInfo.belongingContainers) {
424 if !satisfyAllowedExceptions(mctx, restriction.allowedExceptions, m, dep) {
425 violations = append(violations, restriction.errorMessage)
426 }
427 }
428 }
429 }
430
431 return violations
432}
433
Jihoon Kang17a61d72024-08-12 22:26:52 +0000434func generateContainerInfo(ctx ModuleContext) ContainersInfo {
435 var containers []*container
436
437 for _, cnt := range allContainers {
438 if containerBoundaryFunctionsTable[cnt](ctx) {
439 containers = append(containers, cnt)
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000440 }
441 }
442
443 var belongingApexes []ApexInfo
444 if apexInfo, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
445 belongingApexes = apexInfo.ApexInfos
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000446 }
447
448 return ContainersInfo{
449 belongingContainers: containers,
450 belongingApexes: belongingApexes,
451 }
452}
453
Jihoon Kang224ea082024-08-12 22:38:16 +0000454func getContainerModuleInfo(ctx ModuleContext, module Module) (ContainersInfo, bool) {
455 if ctx.Module() == module {
456 return module.ContainersInfo(), true
457 }
458
459 return OtherModuleProvider(ctx, module, ContainersInfoProvider)
460}
461
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000462func setContainerInfo(ctx ModuleContext) {
Jihoon Kang0f3b1a72024-08-12 22:47:01 +0000463 // Required to determine the unstable container. This provider is set here instead of the
464 // unstableContainerBoundaryFunc in order to prevent setting the provider multiple times.
465 SetProvider(ctx, unstableInfoProvider, unstableInfo{
466 ContainsPlatformPrivateApis: determineUnstableModule(ctx),
467 })
468
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000469 if _, ok := ctx.Module().(InstallableModule); ok {
470 containersInfo := generateContainerInfo(ctx)
Jihoon Kang224ea082024-08-12 22:38:16 +0000471 ctx.Module().base().containersInfo = containersInfo
Jihoon Kangc3d4e112024-06-24 22:16:27 +0000472 SetProvider(ctx, ContainersInfoProvider, containersInfo)
473 }
474}
Jihoon Kang2a884912024-07-01 17:04:46 +0000475
476func checkContainerViolations(ctx ModuleContext) {
477 if _, ok := ctx.Module().(InstallableModule); ok {
478 containersInfo, _ := getContainerModuleInfo(ctx, ctx.Module())
479 ctx.VisitDirectDepsIgnoreBlueprint(func(dep Module) {
480 if !dep.Enabled(ctx) {
481 return
482 }
483
484 // Pre-existing violating dependencies are tracked in containerDependencyViolationAllowlist.
485 // If this dependency is allowlisted, do not check for violation.
486 // If not, check if this dependency matches any restricted dependency and
487 // satisfies any exception functions, which allows bypassing the
488 // restriction. If all of the exceptions are not satisfied, throw an error.
489 if depContainersInfo, ok := getContainerModuleInfo(ctx, dep); ok {
490 if allowedViolations, ok := ContainerDependencyViolationAllowlist[ctx.ModuleName()]; ok && InList(dep.Name(), allowedViolations) {
491 return
492 } else {
493 violations := containersInfo.GetViolations(ctx, ctx.Module(), dep, depContainersInfo)
494 if len(violations) > 0 {
495 errorMessage := fmt.Sprintf("%s cannot depend on %s. ", ctx.ModuleName(), dep.Name())
496 errorMessage += strings.Join(violations, " ")
497 ctx.ModuleErrorf(errorMessage)
498 }
499 }
500 }
501 })
502 }
503}