• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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 (
18	"fmt"
19	"reflect"
20	"slices"
21	"strings"
22
23	"github.com/google/blueprint"
24)
25
26// ----------------------------------------------------------------------------
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, ModuleProxy) bool
35
36type StubsAvailableModule interface {
37	IsStubsModule() bool
38}
39
40// Returns true if the dependency module is a stubs module
41var depIsStubsModule exceptionHandleFunc = func(mctx ModuleContext, _ Module, dep ModuleProxy) bool {
42	return OtherModulePointerProviderOrDefault(mctx, dep, CommonModuleInfoProvider).IsStubsModule
43}
44
45// Returns true if the dependency module belongs to any of the apexes.
46var depIsApexModule exceptionHandleFunc = func(mctx ModuleContext, _ Module, dep ModuleProxy) bool {
47	depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
48	return InList(ApexContainer, depContainersInfo.belongingContainers)
49}
50
51// Returns true if the module and the dependent module belongs to common apexes.
52var belongsToCommonApexes exceptionHandleFunc = func(mctx ModuleContext, m Module, dep ModuleProxy) bool {
53	mContainersInfo, _ := getContainerModuleInfo(mctx, m)
54	depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
55
56	return HasIntersection(mContainersInfo.ApexNames(), depContainersInfo.ApexNames())
57}
58
59// Returns true when all apexes that the module belongs to are non updatable.
60// For an apex module to be allowed to depend on a non-apex partition module,
61// all apexes that the module belong to must be non updatable.
62var belongsToNonUpdatableApex exceptionHandleFunc = func(mctx ModuleContext, m Module, _ ModuleProxy) bool {
63	mContainersInfo, _ := getContainerModuleInfo(mctx, m)
64
65	return !mContainersInfo.UpdatableApex()
66}
67
68// Returns true if the dependency is added via dependency tags that are not used to tag dynamic
69// dependency tags.
70var depIsNotDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m Module, dep ModuleProxy) bool {
71	mInstallable, _ := m.(InstallableModule)
72	depTag := ctx.OtherModuleDependencyTag(dep)
73	return !InList(depTag, mInstallable.DynamicDependencyTags())
74}
75
76// Returns true if the dependency is added via dependency tags that are not used to tag static
77// or dynamic dependency tags. These dependencies do not affect the module in compile time or in
78// runtime, thus are not significant enough to raise an error.
79var depIsNotStaticOrDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m Module, dep ModuleProxy) bool {
80	mInstallable, _ := m.(InstallableModule)
81	depTag := ctx.OtherModuleDependencyTag(dep)
82	return !InList(depTag, append(mInstallable.StaticDependencyTags(), mInstallable.DynamicDependencyTags()...))
83}
84
85var globallyAllowlistedDependencies = []string{
86	// Modules that provide annotations used within the platform and apexes.
87	"aconfig-annotations-lib",
88	"framework-annotations-lib",
89	"unsupportedappusage",
90
91	// TODO(b/363016634): Remove from the allowlist when the module is converted
92	// to java_sdk_library and the java_aconfig_library modules depend on the stub.
93	"aconfig_storage_stub",
94
95	// framework-res provides core resources essential for building apps and system UI.
96	// This module is implicitly added as a dependency for java modules even when the
97	// dependency specifies sdk_version.
98	"framework-res",
99
100	// jacocoagent is implicitly added as a dependency in coverage builds, and is not installed
101	// on the device.
102	"jacocoagent",
103}
104
105// Returns true when the dependency is globally allowlisted for inter-container dependency
106var depIsGloballyAllowlisted exceptionHandleFunc = func(_ ModuleContext, _ Module, dep ModuleProxy) bool {
107	return InList(dep.Name(), globallyAllowlistedDependencies)
108}
109
110// Labels of exception functions, which are used to determine special dependencies that allow
111// otherwise restricted inter-container dependencies
112type exceptionHandleFuncLabel int
113
114const (
115	checkStubs exceptionHandleFuncLabel = iota
116	checkApexModule
117	checkInCommonApexes
118	checkApexIsNonUpdatable
119	checkNotDynamicDepTag
120	checkNotStaticOrDynamicDepTag
121	checkGlobalAllowlistedDep
122)
123
124// Map of [exceptionHandleFuncLabel] to the [exceptionHandleFunc]
125var exceptionHandleFunctionsTable = map[exceptionHandleFuncLabel]exceptionHandleFunc{
126	checkStubs:                    depIsStubsModule,
127	checkApexModule:               depIsApexModule,
128	checkInCommonApexes:           belongsToCommonApexes,
129	checkApexIsNonUpdatable:       belongsToNonUpdatableApex,
130	checkNotDynamicDepTag:         depIsNotDynamicDepTag,
131	checkNotStaticOrDynamicDepTag: depIsNotStaticOrDynamicDepTag,
132	checkGlobalAllowlistedDep:     depIsGloballyAllowlisted,
133}
134
135// ----------------------------------------------------------------------------
136// Start of the definitions of container determination functions.
137//
138// Similar to the above section, below defines the functions used to determine
139// the container of each modules.
140// ----------------------------------------------------------------------------
141
142type containerBoundaryFunc func(mctx ModuleContext) bool
143
144var vendorContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
145	m, ok := mctx.Module().(ImageInterface)
146	return mctx.Module().InstallInVendor() || (ok && m.VendorVariantNeeded(mctx))
147}
148
149var systemContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
150	module := mctx.Module()
151
152	return !module.InstallInTestcases() &&
153		!module.InstallInData() &&
154		!module.InstallInRamdisk() &&
155		!module.InstallInVendorRamdisk() &&
156		!module.InstallInDebugRamdisk() &&
157		!module.InstallInRecovery() &&
158		!module.InstallInVendor() &&
159		!module.InstallInOdm() &&
160		!module.InstallInProduct() &&
161		determineModuleKind(module.base(), mctx.blueprintBaseModuleContext()) == platformModule
162}
163
164var productContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
165	m, ok := mctx.Module().(ImageInterface)
166	return mctx.Module().InstallInProduct() || (ok && m.ProductVariantNeeded(mctx))
167}
168
169var apexContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
170	// TODO(b/394955484): a module can't determine the apexes it belongs to any more
171	return false
172}
173
174var ctsContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
175	props := mctx.Module().GetProperties()
176	for _, prop := range props {
177		val := reflect.ValueOf(prop).Elem()
178		if val.Kind() == reflect.Struct {
179			testSuites := val.FieldByName("Test_suites")
180			if testSuites.IsValid() && testSuites.Kind() == reflect.Slice && slices.Contains(testSuites.Interface().([]string), "cts") {
181				return true
182			}
183		}
184	}
185	return false
186}
187
188type unstableInfo struct {
189	// Determines if the module contains the private APIs of the platform.
190	ContainsPlatformPrivateApis bool
191}
192
193var unstableInfoProvider = blueprint.NewProvider[unstableInfo]()
194
195func determineUnstableModule(mctx ModuleContext) bool {
196	module := mctx.Module()
197
198	unstableModule := module.Name() == "framework-minus-apex"
199	if installable, ok := module.(InstallableModule); ok {
200		for _, staticDepTag := range installable.StaticDependencyTags() {
201			mctx.VisitDirectDepsProxyWithTag(staticDepTag, func(dep ModuleProxy) {
202				if unstableInfo, ok := OtherModuleProvider(mctx, dep, unstableInfoProvider); ok {
203					unstableModule = unstableModule || unstableInfo.ContainsPlatformPrivateApis
204				}
205			})
206		}
207	}
208	return unstableModule
209}
210
211var unstableContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
212	return determineUnstableModule(mctx)
213}
214
215// Map of [*container] to the [containerBoundaryFunc]
216var containerBoundaryFunctionsTable = map[*container]containerBoundaryFunc{
217	VendorContainer:   vendorContainerBoundaryFunc,
218	SystemContainer:   systemContainerBoundaryFunc,
219	ProductContainer:  productContainerBoundaryFunc,
220	ApexContainer:     apexContainerBoundaryFunc,
221	CtsContainer:      ctsContainerBoundaryFunc,
222	UnstableContainer: unstableContainerBoundaryFunc,
223}
224
225// ----------------------------------------------------------------------------
226// End of the definitions of container determination functions.
227// ----------------------------------------------------------------------------
228
229type InstallableModule interface {
230	StaticDependencyTags() []blueprint.DependencyTag
231	DynamicDependencyTags() []blueprint.DependencyTag
232}
233
234type restriction struct {
235	// container of the dependency
236	dependency *container
237
238	// Error message to be emitted to the user when the dependency meets this restriction
239	errorMessage string
240
241	// List of labels of allowed exception functions that allows bypassing this restriction.
242	// If any of the functions mapped to each labels returns true, this dependency would be
243	// considered allowed and an error will not be thrown.
244	allowedExceptions []exceptionHandleFuncLabel
245}
246type container struct {
247	// The name of the container i.e. partition, api domain
248	name string
249
250	// Map of dependency restricted containers.
251	restricted []restriction
252}
253
254var (
255	VendorContainer = &container{
256		name:       VendorVariation,
257		restricted: nil,
258	}
259
260	SystemContainer = &container{
261		name: "system",
262		restricted: []restriction{
263			{
264				dependency: VendorContainer,
265				errorMessage: "Module belonging to the system partition other than HALs is " +
266					"not allowed to depend on the vendor partition module, in order to support " +
267					"independent development/update cycles and to support the Generic System " +
268					"Image. Try depending on HALs, VNDK or AIDL instead.",
269				allowedExceptions: []exceptionHandleFuncLabel{
270					checkStubs,
271					checkNotDynamicDepTag,
272					checkGlobalAllowlistedDep,
273				},
274			},
275		},
276	}
277
278	ProductContainer = &container{
279		name: ProductVariation,
280		restricted: []restriction{
281			{
282				dependency: VendorContainer,
283				errorMessage: "Module belonging to the product partition is not allowed to " +
284					"depend on the vendor partition module, as this may lead to security " +
285					"vulnerabilities. Try depending on the HALs or utilize AIDL instead.",
286				allowedExceptions: []exceptionHandleFuncLabel{
287					checkStubs,
288					checkNotDynamicDepTag,
289					checkGlobalAllowlistedDep,
290				},
291			},
292		},
293	}
294
295	ApexContainer = initializeApexContainer()
296
297	CtsContainer = &container{
298		name: "cts",
299		restricted: []restriction{
300			{
301				dependency: UnstableContainer,
302				errorMessage: "CTS module should not depend on the modules that contain the " +
303					"platform implementation details, including \"framework\". Depending on these " +
304					"modules may lead to disclosure of implementation details and regression " +
305					"due to API changes across platform versions. Try depending on the stubs instead " +
306					"and ensure that the module sets an appropriate 'sdk_version'.",
307				allowedExceptions: []exceptionHandleFuncLabel{
308					checkStubs,
309					checkNotStaticOrDynamicDepTag,
310					checkGlobalAllowlistedDep,
311				},
312			},
313		},
314	}
315
316	// Container signifying that the module contains unstable platform private APIs
317	UnstableContainer = &container{
318		name:       "unstable",
319		restricted: nil,
320	}
321
322	allContainers = []*container{
323		VendorContainer,
324		SystemContainer,
325		ProductContainer,
326		ApexContainer,
327		CtsContainer,
328		UnstableContainer,
329	}
330)
331
332func initializeApexContainer() *container {
333	apexContainer := &container{
334		name: "apex",
335		restricted: []restriction{
336			{
337				dependency: SystemContainer,
338				errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " +
339					"modules belonging to the system partition. Either statically depend on the " +
340					"module or convert the depending module to java_sdk_library and depend on " +
341					"the stubs.",
342				allowedExceptions: []exceptionHandleFuncLabel{
343					checkStubs,
344					checkApexModule,
345					checkInCommonApexes,
346					checkApexIsNonUpdatable,
347					checkNotStaticOrDynamicDepTag,
348					checkGlobalAllowlistedDep,
349				},
350			},
351		},
352	}
353
354	apexContainer.restricted = append(apexContainer.restricted, restriction{
355		dependency: apexContainer,
356		errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " +
357			"modules belonging to other Apex(es). Either include the depending " +
358			"module in the Apex or convert the depending module to java_sdk_library " +
359			"and depend on its stubs.",
360		allowedExceptions: []exceptionHandleFuncLabel{
361			checkStubs,
362			checkInCommonApexes,
363			checkNotStaticOrDynamicDepTag,
364			checkGlobalAllowlistedDep,
365		},
366	})
367
368	return apexContainer
369}
370
371type ContainersInfo struct {
372	belongingContainers []*container
373
374	belongingApexes []ApexInfo
375}
376
377func (c *ContainersInfo) BelongingContainers() []*container {
378	return c.belongingContainers
379}
380
381func (c *ContainersInfo) ApexNames() (ret []string) {
382	for _, apex := range c.belongingApexes {
383		ret = append(ret, apex.BaseApexName)
384	}
385	slices.Sort(ret)
386	return ret
387}
388
389// Returns true if any of the apex the module belongs to is updatable.
390func (c *ContainersInfo) UpdatableApex() bool {
391	for _, apex := range c.belongingApexes {
392		if apex.Updatable {
393			return true
394		}
395	}
396	return false
397}
398
399var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]()
400
401func satisfyAllowedExceptions(ctx ModuleContext, allowedExceptionLabels []exceptionHandleFuncLabel, m Module, dep ModuleProxy) bool {
402	for _, label := range allowedExceptionLabels {
403		if exceptionHandleFunctionsTable[label](ctx, m, dep) {
404			return true
405		}
406	}
407	return false
408}
409
410func (c *ContainersInfo) GetViolations(mctx ModuleContext, m Module, dep ModuleProxy, depInfo ContainersInfo) []string {
411	var violations []string
412
413	// Any containers that the module belongs to but the dependency does not belong to must be examined.
414	_, containersUniqueToModule, _ := ListSetDifference(c.belongingContainers, depInfo.belongingContainers)
415
416	// Apex container should be examined even if both the module and the dependency belong to
417	// the apex container to check that the two modules belong to the same apex.
418	if InList(ApexContainer, c.belongingContainers) && !InList(ApexContainer, containersUniqueToModule) {
419		containersUniqueToModule = append(containersUniqueToModule, ApexContainer)
420	}
421
422	for _, containerUniqueToModule := range containersUniqueToModule {
423		for _, restriction := range containerUniqueToModule.restricted {
424			if InList(restriction.dependency, depInfo.belongingContainers) {
425				if !satisfyAllowedExceptions(mctx, restriction.allowedExceptions, m, dep) {
426					violations = append(violations, restriction.errorMessage)
427				}
428			}
429		}
430	}
431
432	return violations
433}
434
435func generateContainerInfo(ctx ModuleContext) ContainersInfo {
436	var containers []*container
437
438	for _, cnt := range allContainers {
439		if containerBoundaryFunctionsTable[cnt](ctx) {
440			containers = append(containers, cnt)
441		}
442	}
443
444	return ContainersInfo{
445		belongingContainers: containers,
446		// TODO(b/394955484): a module can't determine the apexes it belongs to any more
447		belongingApexes: nil,
448	}
449}
450
451func getContainerModuleInfo(ctx ModuleContext, module Module) (ContainersInfo, bool) {
452	if EqualModules(ctx.Module(), module) {
453		return ctx.getContainersInfo(), true
454	}
455
456	return OtherModuleProvider(ctx, module, ContainersInfoProvider)
457}
458
459func setContainerInfo(ctx ModuleContext) {
460	// Required to determine the unstable container. This provider is set here instead of the
461	// unstableContainerBoundaryFunc in order to prevent setting the provider multiple times.
462	SetProvider(ctx, unstableInfoProvider, unstableInfo{
463		ContainsPlatformPrivateApis: determineUnstableModule(ctx),
464	})
465
466	if _, ok := ctx.Module().(InstallableModule); ok {
467		containersInfo := generateContainerInfo(ctx)
468		ctx.setContainersInfo(containersInfo)
469		SetProvider(ctx, ContainersInfoProvider, containersInfo)
470	}
471}
472
473func checkContainerViolations(ctx ModuleContext) {
474	if _, ok := ctx.Module().(InstallableModule); ok {
475		containersInfo, _ := getContainerModuleInfo(ctx, ctx.Module())
476		ctx.VisitDirectDepsProxy(func(dep ModuleProxy) {
477			if !OtherModuleProviderOrDefault(ctx, dep, CommonModuleInfoProvider).Enabled {
478				return
479			}
480
481			// Pre-existing violating dependencies are tracked in containerDependencyViolationAllowlist.
482			// If this dependency is allowlisted, do not check for violation.
483			// If not, check if this dependency matches any restricted dependency and
484			// satisfies any exception functions, which allows bypassing the
485			// restriction. If all of the exceptions are not satisfied, throw an error.
486			if depContainersInfo, ok := getContainerModuleInfo(ctx, dep); ok {
487				if allowedViolations, ok := ContainerDependencyViolationAllowlist[ctx.ModuleName()]; ok && InList(dep.Name(), allowedViolations) {
488					return
489				} else {
490					violations := containersInfo.GetViolations(ctx, ctx.Module(), dep, depContainersInfo)
491					if len(violations) > 0 {
492						errorMessage := fmt.Sprintf("%s cannot depend on %s. ", ctx.ModuleName(), dep.Name())
493						errorMessage += strings.Join(violations, " ")
494						ctx.ModuleErrorf(errorMessage)
495					}
496				}
497			}
498		})
499	}
500}
501