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