xref: /aosp_15_r20/system/sepolicy/build/soong/selinux_contexts.go (revision e4a36f4174b17bbab9dc043f4a65dc8d87377290)
1// Copyright (C) 2019 The Android Open Source Project
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 selinux
16
17import (
18	"fmt"
19	"io"
20
21	"github.com/google/blueprint"
22	"github.com/google/blueprint/proptools"
23
24	"android/soong/android"
25	"android/soong/sysprop"
26)
27
28type selinuxContextsProperties struct {
29	// Filenames under sepolicy directories, which will be used to generate contexts file.
30	Srcs []string `android:"path"`
31
32	// Output file name. Defaults to module name
33	Stem *string
34
35	Product_variables struct {
36		Address_sanitize struct {
37			Srcs []string `android:"path"`
38		}
39	}
40
41	// Whether the comments in generated contexts file will be removed or not.
42	Remove_comment *bool
43
44	// Whether the result context file is sorted with fc_sort or not.
45	Fc_sort *bool
46
47	// Make this module available when building for recovery
48	Recovery_available *bool
49
50	// Board api level of policy files. Set "current" for RELEASE_BOARD_API_LEVEL, or a direct
51	// version string (e.g. "202404"). Defaults to "current"
52	Board_api_level *string
53}
54
55type seappProperties struct {
56	// Files containing neverallow rules.
57	Neverallow_files []string `android:"path"`
58
59	// Precompiled sepolicy binary file which will be fed to checkseapp.
60	Sepolicy *string `android:"path"`
61}
62
63type selinuxContextsModule struct {
64	android.ModuleBase
65	android.DefaultableModuleBase
66	flaggableModuleBase
67
68	properties      selinuxContextsProperties
69	seappProperties seappProperties
70	build           func(ctx android.ModuleContext, inputs android.Paths) android.Path
71	deps            func(ctx android.BottomUpMutatorContext)
72	outputPath      android.Path
73	installPath     android.InstallPath
74}
75
76var _ flaggableModule = (*selinuxContextsModule)(nil)
77
78var (
79	reuseContextsDepTag  = dependencyTag{name: "reuseContexts"}
80	syspropLibraryDepTag = dependencyTag{name: "sysprop_library"}
81)
82
83func init() {
84	pctx.HostBinToolVariable("fc_sort", "fc_sort")
85
86	android.RegisterModuleType("contexts_defaults", contextsDefaultsFactory)
87	android.RegisterModuleType("file_contexts", fileFactory)
88	android.RegisterModuleType("hwservice_contexts", hwServiceFactory)
89	android.RegisterModuleType("property_contexts", propertyFactory)
90	android.RegisterModuleType("service_contexts", serviceFactory)
91	android.RegisterModuleType("keystore2_key_contexts", keystoreKeyFactory)
92	android.RegisterModuleType("seapp_contexts", seappFactory)
93	android.RegisterModuleType("vndservice_contexts", vndServiceFactory)
94	android.RegisterModuleType("tee_service_contexts", teeServiceFactory)
95
96	android.RegisterModuleType("file_contexts_test", fileContextsTestFactory)
97	android.RegisterModuleType("property_contexts_test", propertyContextsTestFactory)
98	android.RegisterModuleType("hwservice_contexts_test", hwserviceContextsTestFactory)
99	android.RegisterModuleType("service_contexts_test", serviceContextsTestFactory)
100	android.RegisterModuleType("vndservice_contexts_test", vndServiceContextsTestFactory)
101}
102
103func (m *selinuxContextsModule) InstallInRoot() bool {
104	return m.InRecovery()
105}
106
107func (m *selinuxContextsModule) InstallInRecovery() bool {
108	// ModuleBase.InRecovery() checks the image variant
109	return m.InRecovery()
110}
111
112func (m *selinuxContextsModule) onlyInRecovery() bool {
113	// ModuleBase.InstallInRecovery() checks commonProperties.Recovery property
114	return m.ModuleBase.InstallInRecovery()
115}
116
117func (m *selinuxContextsModule) DepsMutator(ctx android.BottomUpMutatorContext) {
118	m.flagDeps(ctx)
119
120	if m.deps != nil {
121		m.deps(ctx)
122	}
123
124	if m.InRecovery() && !m.onlyInRecovery() {
125		ctx.AddFarVariationDependencies([]blueprint.Variation{
126			{Mutator: "image", Variation: android.CoreVariation},
127		}, reuseContextsDepTag, ctx.ModuleName())
128	}
129}
130
131func (m *selinuxContextsModule) propertyContextsDeps(ctx android.BottomUpMutatorContext) {
132	for _, lib := range sysprop.SyspropLibraries(ctx.Config()) {
133		ctx.AddFarVariationDependencies([]blueprint.Variation{}, syspropLibraryDepTag, lib)
134	}
135}
136
137func (m *selinuxContextsModule) stem() string {
138	return proptools.StringDefault(m.properties.Stem, m.Name())
139}
140
141func (m *selinuxContextsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
142	if m.InRecovery() {
143		// Installing context files at the root of the recovery partition
144		m.installPath = android.PathForModuleInstall(ctx)
145	} else {
146		m.installPath = android.PathForModuleInstall(ctx, "etc", "selinux")
147	}
148
149	if m.InRecovery() && !m.onlyInRecovery() {
150		dep := ctx.GetDirectDepWithTag(m.Name(), reuseContextsDepTag)
151
152		if reuseDeps, ok := dep.(*selinuxContextsModule); ok {
153			m.outputPath = reuseDeps.outputPath
154			ctx.InstallFile(m.installPath, m.stem(), m.outputPath)
155			return
156		}
157	}
158
159	m.outputPath = m.build(ctx, android.PathsForModuleSrc(ctx, m.properties.Srcs))
160	ctx.InstallFile(m.installPath, m.stem(), m.outputPath)
161
162	ctx.SetOutputFiles([]android.Path{m.outputPath}, "")
163}
164
165func newModule() *selinuxContextsModule {
166	m := &selinuxContextsModule{}
167	m.AddProperties(
168		&m.properties,
169		&m.seappProperties,
170	)
171	initFlaggableModule(m)
172	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
173	android.InitDefaultableModule(m)
174	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
175		m.selinuxContextsHook(ctx)
176	})
177	return m
178}
179
180type contextsDefaults struct {
181	android.ModuleBase
182	android.DefaultsModuleBase
183}
184
185// contexts_defaults provides a set of properties that can be inherited by other contexts modules.
186// (file_contexts, property_contexts, seapp_contexts, etc.) A module can use the properties from a
187// contexts_defaults using `defaults: ["<:default_module_name>"]`. Properties of both modules are
188// erged (when possible) by prepending the default module's values to the depending module's values.
189func contextsDefaultsFactory() android.Module {
190	m := &contextsDefaults{}
191	m.AddProperties(
192		&selinuxContextsProperties{},
193		&seappProperties{},
194		&flaggableModuleProperties{},
195	)
196	android.InitDefaultsModule(m)
197	return m
198}
199
200func (m *selinuxContextsModule) selinuxContextsHook(ctx android.LoadHookContext) {
201	// TODO: clean this up to use build/soong/android/variable.go after b/79249983
202	var srcs []string
203
204	for _, sanitize := range ctx.Config().SanitizeDevice() {
205		if sanitize == "address" {
206			srcs = append(srcs, m.properties.Product_variables.Address_sanitize.Srcs...)
207			break
208		}
209	}
210
211	m.properties.Srcs = append(m.properties.Srcs, srcs...)
212}
213
214func (m *selinuxContextsModule) AndroidMk() android.AndroidMkData {
215	nameSuffix := ""
216	if m.InRecovery() && !m.onlyInRecovery() {
217		nameSuffix = ".recovery"
218	}
219	return android.AndroidMkData{
220		Class:      "ETC",
221		OutputFile: android.OptionalPathForPath(m.outputPath),
222		SubName:    nameSuffix,
223		Extra: []android.AndroidMkExtraFunc{
224			func(w io.Writer, outputFile android.Path) {
225				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", m.installPath.String())
226				fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", m.stem())
227			},
228		},
229	}
230}
231
232func (m *selinuxContextsModule) ImageMutatorBegin(ctx android.ImageInterfaceContext) {
233	if proptools.Bool(m.properties.Recovery_available) && m.ModuleBase.InstallInRecovery() {
234		ctx.PropertyErrorf("recovery_available",
235			"doesn't make sense at the same time as `recovery: true`")
236	}
237}
238
239func (m *selinuxContextsModule) VendorVariantNeeded(ctx android.ImageInterfaceContext) bool {
240	return false
241}
242
243func (m *selinuxContextsModule) ProductVariantNeeded(ctx android.ImageInterfaceContext) bool {
244	return false
245}
246
247func (m *selinuxContextsModule) CoreVariantNeeded(ctx android.ImageInterfaceContext) bool {
248	return !m.ModuleBase.InstallInRecovery()
249}
250
251func (m *selinuxContextsModule) RamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
252	return false
253}
254
255func (m *selinuxContextsModule) VendorRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
256	return false
257}
258
259func (m *selinuxContextsModule) DebugRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
260	return false
261}
262
263func (m *selinuxContextsModule) RecoveryVariantNeeded(ctx android.ImageInterfaceContext) bool {
264	return m.ModuleBase.InstallInRecovery() || proptools.Bool(m.properties.Recovery_available)
265}
266
267func (m *selinuxContextsModule) ExtraImageVariations(ctx android.ImageInterfaceContext) []string {
268	return nil
269}
270
271func (m *selinuxContextsModule) SetImageVariation(ctx android.ImageInterfaceContext, variation string) {
272}
273
274var _ android.ImageInterface = (*selinuxContextsModule)(nil)
275
276func (m *selinuxContextsModule) buildGeneralContexts(ctx android.ModuleContext, inputs android.Paths) android.Path {
277	builtContext := pathForModuleOut(ctx, ctx.ModuleName()+"_m4out")
278
279	rule := android.NewRuleBuilder(pctx, ctx)
280
281	newlineFile := pathForModuleOut(ctx, "newline")
282
283	rule.Command().Text("echo").FlagWithOutput("> ", newlineFile)
284	rule.Temporary(newlineFile)
285
286	var inputsWithNewline android.Paths
287	for _, input := range inputs {
288		inputsWithNewline = append(inputsWithNewline, input, newlineFile)
289	}
290
291	flags := m.getBuildFlags(ctx)
292	rule.Command().
293		Tool(ctx.Config().PrebuiltBuildTool(ctx, "m4")).
294		Text("--fatal-warnings -s").
295		FlagForEachArg("-D", ctx.DeviceConfig().SepolicyM4Defs()).
296		Flag(boardApiLevelToM4Macro(ctx, m.properties.Board_api_level)).
297		Flags(flagsToM4Macros(flags)).
298		Inputs(inputsWithNewline).
299		FlagWithOutput("> ", builtContext)
300
301	if proptools.Bool(m.properties.Remove_comment) {
302		rule.Temporary(builtContext)
303
304		remove_comment_output := pathForModuleOut(ctx, ctx.ModuleName()+"_remove_comment")
305
306		rule.Command().
307			Text("sed -e 's/#.*$//' -e '/^$/d'").
308			Input(builtContext).
309			FlagWithOutput("> ", remove_comment_output)
310
311		builtContext = remove_comment_output
312	}
313
314	if proptools.Bool(m.properties.Fc_sort) {
315		rule.Temporary(builtContext)
316
317		sorted_output := pathForModuleOut(ctx, ctx.ModuleName()+"_sorted")
318
319		rule.Command().
320			Tool(ctx.Config().HostToolPath(ctx, "fc_sort")).
321			FlagWithInput("-i ", builtContext).
322			FlagWithOutput("-o ", sorted_output)
323
324		builtContext = sorted_output
325	}
326
327	ret := pathForModuleOut(ctx, m.stem())
328	rule.Temporary(builtContext)
329	rule.Command().Text("cp").Input(builtContext).Output(ret)
330
331	rule.DeleteTemporaryFiles()
332	rule.Build("selinux_contexts", "building contexts: "+m.Name())
333
334	return ret
335}
336
337func (m *selinuxContextsModule) buildFileContexts(ctx android.ModuleContext, inputs android.Paths) android.Path {
338	if m.properties.Remove_comment == nil {
339		m.properties.Remove_comment = proptools.BoolPtr(true)
340	}
341	return m.buildGeneralContexts(ctx, inputs)
342}
343
344func fileFactory() android.Module {
345	m := newModule()
346	m.build = m.buildFileContexts
347	return m
348}
349
350func (m *selinuxContextsModule) buildServiceContexts(ctx android.ModuleContext, inputs android.Paths) android.Path {
351	if m.properties.Remove_comment == nil {
352		m.properties.Remove_comment = proptools.BoolPtr(true)
353	}
354
355	return m.buildGeneralContexts(ctx, inputs)
356}
357
358func (m *selinuxContextsModule) checkVendorPropertyNamespace(ctx android.ModuleContext, input android.Path) android.Path {
359	shippingApiLevel := ctx.DeviceConfig().ShippingApiLevel()
360	ApiLevelR := android.ApiLevelOrPanic(ctx, "R")
361
362	rule := android.NewRuleBuilder(pctx, ctx)
363
364	// This list is from vts_treble_sys_prop_test.
365	allowedPropertyPrefixes := []string{
366		"ctl.odm.",
367		"ctl.vendor.",
368		"ctl.start$odm.",
369		"ctl.start$vendor.",
370		"ctl.stop$odm.",
371		"ctl.stop$vendor.",
372		"init.svc.odm.",
373		"init.svc.vendor.",
374		"ro.boot.",
375		"ro.hardware.",
376		"ro.odm.",
377		"ro.vendor.",
378		"odm.",
379		"persist.odm.",
380		"persist.vendor.",
381		"vendor.",
382	}
383
384	// persist.camera is also allowed for devices launching with R or eariler
385	if shippingApiLevel.LessThanOrEqualTo(ApiLevelR) {
386		allowedPropertyPrefixes = append(allowedPropertyPrefixes, "persist.camera.")
387	}
388
389	var allowedContextPrefixes []string
390
391	if shippingApiLevel.GreaterThanOrEqualTo(ApiLevelR) {
392		// This list is from vts_treble_sys_prop_test.
393		allowedContextPrefixes = []string{
394			"vendor_",
395			"odm_",
396		}
397	}
398
399	cmd := rule.Command().
400		BuiltTool("check_prop_prefix").
401		FlagWithInput("--property-contexts ", input).
402		FlagForEachArg("--allowed-property-prefix ", proptools.ShellEscapeList(allowedPropertyPrefixes)). // contains shell special character '$'
403		FlagForEachArg("--allowed-context-prefix ", allowedContextPrefixes)
404
405	if !ctx.DeviceConfig().BuildBrokenVendorPropertyNamespace() {
406		cmd.Flag("--strict")
407	}
408
409	out := pathForModuleOut(ctx, ctx.ModuleName()+"_namespace_checked")
410	rule.Command().Text("cp -f").Input(input).Output(out)
411	rule.Build("check_namespace", "checking namespace of "+ctx.ModuleName())
412	return out
413}
414
415func (m *selinuxContextsModule) buildPropertyContexts(ctx android.ModuleContext, inputs android.Paths) android.Path {
416	// vendor/odm properties are enforced for devices launching with Android Q or later. So, if
417	// vendor/odm, make sure that only vendor/odm properties exist.
418	builtCtxFile := m.buildGeneralContexts(ctx, inputs)
419
420	shippingApiLevel := ctx.DeviceConfig().ShippingApiLevel()
421	ApiLevelQ := android.ApiLevelOrPanic(ctx, "Q")
422	if (ctx.SocSpecific() || ctx.DeviceSpecific()) && shippingApiLevel.GreaterThanOrEqualTo(ApiLevelQ) {
423		builtCtxFile = m.checkVendorPropertyNamespace(ctx, builtCtxFile)
424	}
425
426	var apiFiles android.Paths
427	ctx.VisitDirectDepsWithTag(syspropLibraryDepTag, func(c android.Module) {
428		i, ok := c.(interface{ CurrentSyspropApiFile() android.OptionalPath })
429		if !ok {
430			panic(fmt.Errorf("unknown dependency %q for %q", ctx.OtherModuleName(c), ctx.ModuleName()))
431		}
432		if api := i.CurrentSyspropApiFile(); api.Valid() {
433			apiFiles = append(apiFiles, api.Path())
434		}
435	})
436
437	// check compatibility with sysprop_library
438	if len(apiFiles) > 0 {
439		out := pathForModuleOut(ctx, ctx.ModuleName()+"_api_checked")
440		rule := android.NewRuleBuilder(pctx, ctx)
441
442		msg := `\n******************************\n` +
443			`API of sysprop_library doesn't match with property_contexts\n` +
444			`Please fix the breakage and rebuild.\n` +
445			`******************************\n`
446
447		rule.Command().
448			Text("( ").
449			BuiltTool("sysprop_type_checker").
450			FlagForEachInput("--api ", apiFiles).
451			FlagWithInput("--context ", builtCtxFile).
452			Text(" || ( echo").Flag("-e").
453			Flag(`"` + msg + `"`).
454			Text("; exit 38) )")
455
456		rule.Command().Text("cp -f").Input(builtCtxFile).Output(out)
457		rule.Build("property_contexts_check_api", "checking API: "+m.Name())
458		builtCtxFile = out
459	}
460
461	return builtCtxFile
462}
463
464func (m *selinuxContextsModule) shouldCheckCoredomain(ctx android.ModuleContext) bool {
465	if !ctx.SocSpecific() && !ctx.DeviceSpecific() {
466		return false
467	}
468
469	return ctx.DeviceConfig().CheckVendorSeappViolations()
470}
471
472func (m *selinuxContextsModule) buildSeappContexts(ctx android.ModuleContext, inputs android.Paths) android.Path {
473	neverallowFile := pathForModuleOut(ctx, "neverallow")
474	ret := pathForModuleOut(ctx, "checkseapp", m.stem())
475
476	// Step 1. Generate a M4 processed neverallow file
477	flags := m.getBuildFlags(ctx)
478	m4NeverallowFile := pathForModuleOut(ctx, "neverallow.m4out")
479	rule := android.NewRuleBuilder(pctx, ctx)
480	rule.Command().
481		Tool(ctx.Config().PrebuiltBuildTool(ctx, "m4")).
482		Flag("--fatal-warnings").
483		FlagForEachArg("-D", ctx.DeviceConfig().SepolicyM4Defs()).
484		Flags(flagsToM4Macros(flags)).
485		Inputs(android.PathsForModuleSrc(ctx, m.seappProperties.Neverallow_files)).
486		FlagWithOutput("> ", m4NeverallowFile)
487
488	rule.Temporary(m4NeverallowFile)
489	rule.Command().
490		Text("( grep").
491		Flag("-ihe").
492		Text("'^neverallow'").
493		Input(m4NeverallowFile).
494		Text(">").
495		Output(neverallowFile).
496		Text("|| true )") // to make ninja happy even when result is empty
497
498	// Step 2. Generate a M4 processed contexts file
499	builtCtx := m.buildGeneralContexts(ctx, inputs)
500
501	// Step 3. checkseapp
502	rule.Temporary(neverallowFile)
503	checkCmd := rule.Command().BuiltTool("checkseapp").
504		FlagWithInput("-p ", android.PathForModuleSrc(ctx, proptools.String(m.seappProperties.Sepolicy))).
505		FlagWithOutput("-o ", ret).
506		Input(builtCtx).
507		Input(neverallowFile)
508
509	if m.shouldCheckCoredomain(ctx) {
510		checkCmd.Flag("-c") // check coredomain for vendor contexts
511	}
512
513	rule.Build("seapp_contexts", "Building seapp_contexts: "+m.Name())
514	return ret
515}
516
517func hwServiceFactory() android.Module {
518	m := newModule()
519	m.build = m.buildServiceContexts
520	return m
521}
522
523func propertyFactory() android.Module {
524	m := newModule()
525	m.build = m.buildPropertyContexts
526	m.deps = m.propertyContextsDeps
527	return m
528}
529
530func serviceFactory() android.Module {
531	m := newModule()
532	m.build = m.buildServiceContexts
533	return m
534}
535
536func keystoreKeyFactory() android.Module {
537	m := newModule()
538	m.build = m.buildGeneralContexts
539	return m
540}
541
542func teeServiceFactory() android.Module {
543	m := newModule()
544	m.build = m.buildGeneralContexts
545	return m
546}
547
548func seappFactory() android.Module {
549	m := newModule()
550	m.build = m.buildSeappContexts
551	return m
552}
553
554func vndServiceFactory() android.Module {
555	m := newModule()
556	m.build = m.buildGeneralContexts
557	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
558		if !ctx.SocSpecific() {
559			ctx.ModuleErrorf(m.Name(), "must set vendor: true")
560			return
561		}
562	})
563	return m
564}
565
566type contextsTestProperties struct {
567	// Contexts files to be tested.
568	Srcs []string `android:"path"`
569
570	// Precompiled sepolicy binary to be tesed together.
571	Sepolicy *string `android:"path"`
572}
573
574type fileContextsTestProperties struct {
575	// Test data. File passed to `checkfc -t` to validate how contexts are resolved.
576	Test_data *string `android:"path"`
577}
578
579type contextsTestModule struct {
580	android.ModuleBase
581
582	// The type of context.
583	context contextType
584
585	properties     contextsTestProperties
586	fileProperties fileContextsTestProperties
587	testTimestamp  android.OutputPath
588}
589
590type contextType int
591
592const (
593	FileContext contextType = iota
594	PropertyContext
595	ServiceContext
596	HwServiceContext
597	VndServiceContext
598)
599
600// checkfc parses a context file and checks for syntax errors.
601// If -s is specified, the service backend is used to verify binder services.
602// If -l is specified, the service backend is used to verify hwbinder services.
603// Otherwise, context_file is assumed to be a file_contexts file
604// If -e is specified, then the context_file is allowed to be empty.
605
606// file_contexts_test tests given file_contexts files with checkfc.
607func fileContextsTestFactory() android.Module {
608	m := &contextsTestModule{context: FileContext}
609	m.AddProperties(&m.properties)
610	m.AddProperties(&m.fileProperties)
611	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
612	return m
613}
614
615// property_contexts_test tests given property_contexts files with property_info_checker.
616func propertyContextsTestFactory() android.Module {
617	m := &contextsTestModule{context: PropertyContext}
618	m.AddProperties(&m.properties)
619	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
620	return m
621}
622
623// hwservice_contexts_test tests given hwservice_contexts files with checkfc.
624func hwserviceContextsTestFactory() android.Module {
625	m := &contextsTestModule{context: HwServiceContext}
626	m.AddProperties(&m.properties)
627	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
628	return m
629}
630
631// service_contexts_test tests given service_contexts files with checkfc.
632func serviceContextsTestFactory() android.Module {
633	// checkfc -s: service_contexts test
634	m := &contextsTestModule{context: ServiceContext}
635	m.AddProperties(&m.properties)
636	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
637	return m
638}
639
640// vndservice_contexts_test tests given vndservice_contexts files with checkfc.
641func vndServiceContextsTestFactory() android.Module {
642	m := &contextsTestModule{context: VndServiceContext}
643	m.AddProperties(&m.properties)
644	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
645	return m
646}
647
648func (m *contextsTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
649	tool := "checkfc"
650	if m.context == PropertyContext {
651		tool = "property_info_checker"
652	}
653
654	if len(m.properties.Srcs) == 0 {
655		ctx.PropertyErrorf("srcs", "can't be empty")
656		return
657	}
658
659	validateWithPolicy := true
660	if proptools.String(m.properties.Sepolicy) == "" {
661		if m.context == FileContext {
662			if proptools.String(m.fileProperties.Test_data) == "" {
663				ctx.PropertyErrorf("test_data", "Either test_data or sepolicy should be provided")
664				return
665			}
666			validateWithPolicy = false
667		} else {
668			ctx.PropertyErrorf("sepolicy", "can't be empty")
669			return
670		}
671	}
672
673	flags := []string(nil)
674	switch m.context {
675	case FileContext:
676		if !validateWithPolicy {
677			flags = []string{"-t"}
678		}
679	case ServiceContext:
680		flags = []string{"-s" /* binder services */}
681	case HwServiceContext:
682		flags = []string{"-e" /* allow empty */, "-l" /* hwbinder services */}
683	case VndServiceContext:
684		flags = []string{"-e" /* allow empty */, "-v" /* vnd service */}
685	}
686
687	srcs := android.PathsForModuleSrc(ctx, m.properties.Srcs)
688	rule := android.NewRuleBuilder(pctx, ctx)
689
690	if validateWithPolicy {
691		sepolicy := android.PathForModuleSrc(ctx, proptools.String(m.properties.Sepolicy))
692		rule.Command().BuiltTool(tool).
693			Flags(flags).
694			Input(sepolicy).
695			Inputs(srcs)
696	} else {
697		test_data := android.PathForModuleSrc(ctx, proptools.String(m.fileProperties.Test_data))
698		rule.Command().BuiltTool(tool).
699			Flags(flags).
700			Inputs(srcs).
701			Input(test_data)
702	}
703
704	m.testTimestamp = pathForModuleOut(ctx, "timestamp")
705	rule.Command().Text("touch").Output(m.testTimestamp)
706	rule.Build("contexts_test", "running contexts test: "+ctx.ModuleName())
707}
708
709func (m *contextsTestModule) AndroidMkEntries() []android.AndroidMkEntries {
710	return []android.AndroidMkEntries{android.AndroidMkEntries{
711		Class: "FAKE",
712		// OutputFile is needed, even though BUILD_PHONY_PACKAGE doesn't use it.
713		// Without OutputFile this module won't be exported to Makefile.
714		OutputFile: android.OptionalPathForPath(m.testTimestamp),
715		Include:    "$(BUILD_PHONY_PACKAGE)",
716		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
717			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
718				entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", m.testTimestamp.String())
719			},
720		},
721	}}
722}
723
724// contextsTestModule implements ImageInterface to be able to include recovery_available contexts
725// modules as its sources.
726func (m *contextsTestModule) ImageMutatorBegin(ctx android.ImageInterfaceContext) {
727}
728
729func (m *contextsTestModule) VendorVariantNeeded(ctx android.ImageInterfaceContext) bool {
730	return false
731}
732
733func (m *contextsTestModule) ProductVariantNeeded(ctx android.ImageInterfaceContext) bool {
734	return false
735}
736
737func (m *contextsTestModule) CoreVariantNeeded(ctx android.ImageInterfaceContext) bool {
738	return true
739}
740
741func (m *contextsTestModule) RamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
742	return false
743}
744
745func (m *contextsTestModule) VendorRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
746	return false
747}
748
749func (m *contextsTestModule) DebugRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
750	return false
751}
752
753func (m *contextsTestModule) RecoveryVariantNeeded(ctx android.ImageInterfaceContext) bool {
754	return false
755}
756
757func (m *contextsTestModule) ExtraImageVariations(ctx android.ImageInterfaceContext) []string {
758	return nil
759}
760
761func (m *contextsTestModule) SetImageVariation(ctx android.ImageInterfaceContext, variation string) {
762}
763
764var _ android.ImageInterface = (*contextsTestModule)(nil)
765