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 "os" 21 "strings" 22 23 "github.com/google/blueprint" 24 "github.com/google/blueprint/proptools" 25 26 "android/soong/android" 27 "android/soong/sysprop" 28) 29 30type selinuxContextsProperties struct { 31 // Filenames under sepolicy directories, which will be used to generate contexts file. 32 Srcs []string `android:"path"` 33 34 // Output file name. Defaults to module name 35 Stem *string 36 37 Product_variables struct { 38 Address_sanitize struct { 39 Srcs []string `android:"path"` 40 } 41 } 42 43 // Whether the comments in generated contexts file will be removed or not. 44 Remove_comment *bool 45 46 // Whether the result context file is sorted with fc_sort or not. 47 Fc_sort *bool 48 49 // Make this module available when building for recovery 50 Recovery_available *bool 51} 52 53type fileContextsProperties struct { 54 // flatten_apex can be used to specify additional sources of file_contexts. 55 // Apex paths, /system/apex/{apex_name}, will be amended to the paths of file_contexts 56 // entries. 57 Flatten_apex struct { 58 Srcs []string `android:"path"` 59 } 60} 61 62type seappProperties struct { 63 // Files containing neverallow rules. 64 Neverallow_files []string `android:"path"` 65 66 // Precompiled sepolicy binary file which will be fed to checkseapp. 67 Sepolicy *string `android:"path"` 68} 69 70type selinuxContextsModule struct { 71 android.ModuleBase 72 73 properties selinuxContextsProperties 74 fileContextsProperties fileContextsProperties 75 seappProperties seappProperties 76 build func(ctx android.ModuleContext, inputs android.Paths) android.Path 77 deps func(ctx android.BottomUpMutatorContext) 78 outputPath android.Path 79 installPath android.InstallPath 80} 81 82var ( 83 reuseContextsDepTag = dependencyTag{name: "reuseContexts"} 84 syspropLibraryDepTag = dependencyTag{name: "sysprop_library"} 85) 86 87func init() { 88 pctx.HostBinToolVariable("fc_sort", "fc_sort") 89 90 android.RegisterModuleType("file_contexts", fileFactory) 91 android.RegisterModuleType("hwservice_contexts", hwServiceFactory) 92 android.RegisterModuleType("property_contexts", propertyFactory) 93 android.RegisterModuleType("service_contexts", serviceFactory) 94 android.RegisterModuleType("keystore2_key_contexts", keystoreKeyFactory) 95 android.RegisterModuleType("seapp_contexts", seappFactory) 96 android.RegisterModuleType("vndservice_contexts", vndServiceFactory) 97 98 android.RegisterModuleType("file_contexts_test", fileContextsTestFactory) 99 android.RegisterModuleType("property_contexts_test", propertyContextsTestFactory) 100 android.RegisterModuleType("hwservice_contexts_test", hwserviceContextsTestFactory) 101 android.RegisterModuleType("service_contexts_test", serviceContextsTestFactory) 102 android.RegisterModuleType("vndservice_contexts_test", vndServiceContextsTestFactory) 103} 104 105func (m *selinuxContextsModule) InstallInRoot() bool { 106 return m.InRecovery() 107} 108 109func (m *selinuxContextsModule) InstallInRecovery() bool { 110 // ModuleBase.InRecovery() checks the image variant 111 return m.InRecovery() 112} 113 114func (m *selinuxContextsModule) onlyInRecovery() bool { 115 // ModuleBase.InstallInRecovery() checks commonProperties.Recovery property 116 return m.ModuleBase.InstallInRecovery() 117} 118 119func (m *selinuxContextsModule) DepsMutator(ctx android.BottomUpMutatorContext) { 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 163func newModule() *selinuxContextsModule { 164 m := &selinuxContextsModule{} 165 m.AddProperties( 166 &m.properties, 167 &m.fileContextsProperties, 168 &m.seappProperties, 169 ) 170 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 171 android.AddLoadHook(m, func(ctx android.LoadHookContext) { 172 m.selinuxContextsHook(ctx) 173 }) 174 return m 175} 176 177func (m *selinuxContextsModule) selinuxContextsHook(ctx android.LoadHookContext) { 178 // TODO: clean this up to use build/soong/android/variable.go after b/79249983 179 var srcs []string 180 181 for _, sanitize := range ctx.Config().SanitizeDevice() { 182 if sanitize == "address" { 183 srcs = append(srcs, m.properties.Product_variables.Address_sanitize.Srcs...) 184 break 185 } 186 } 187 188 m.properties.Srcs = append(m.properties.Srcs, srcs...) 189} 190 191func (m *selinuxContextsModule) AndroidMk() android.AndroidMkData { 192 nameSuffix := "" 193 if m.InRecovery() && !m.onlyInRecovery() { 194 nameSuffix = ".recovery" 195 } 196 return android.AndroidMkData{ 197 Class: "ETC", 198 OutputFile: android.OptionalPathForPath(m.outputPath), 199 SubName: nameSuffix, 200 Extra: []android.AndroidMkExtraFunc{ 201 func(w io.Writer, outputFile android.Path) { 202 fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", m.installPath.String()) 203 fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", m.stem()) 204 }, 205 }, 206 } 207} 208 209func (m *selinuxContextsModule) ImageMutatorBegin(ctx android.BaseModuleContext) { 210 if proptools.Bool(m.properties.Recovery_available) && m.ModuleBase.InstallInRecovery() { 211 ctx.PropertyErrorf("recovery_available", 212 "doesn't make sense at the same time as `recovery: true`") 213 } 214} 215 216func (m *selinuxContextsModule) CoreVariantNeeded(ctx android.BaseModuleContext) bool { 217 return !m.ModuleBase.InstallInRecovery() 218} 219 220func (m *selinuxContextsModule) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 221 return false 222} 223 224func (m *selinuxContextsModule) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 225 return false 226} 227 228func (m *selinuxContextsModule) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 229 return false 230} 231 232func (m *selinuxContextsModule) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { 233 return m.ModuleBase.InstallInRecovery() || proptools.Bool(m.properties.Recovery_available) 234} 235 236func (m *selinuxContextsModule) ExtraImageVariations(ctx android.BaseModuleContext) []string { 237 return nil 238} 239 240func (m *selinuxContextsModule) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) { 241} 242 243var _ android.ImageInterface = (*selinuxContextsModule)(nil) 244 245func (m *selinuxContextsModule) buildGeneralContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 246 builtContext := android.PathForModuleGen(ctx, ctx.ModuleName()+"_m4out") 247 248 rule := android.NewRuleBuilder(pctx, ctx) 249 250 rule.Command(). 251 Tool(ctx.Config().PrebuiltBuildTool(ctx, "m4")). 252 Text("--fatal-warnings -s"). 253 FlagForEachArg("-D", ctx.DeviceConfig().SepolicyM4Defs()). 254 Inputs(inputs). 255 FlagWithOutput("> ", builtContext) 256 257 if proptools.Bool(m.properties.Remove_comment) { 258 rule.Temporary(builtContext) 259 260 remove_comment_output := android.PathForModuleGen(ctx, ctx.ModuleName()+"_remove_comment") 261 262 rule.Command(). 263 Text("sed -e 's/#.*$//' -e '/^$/d'"). 264 Input(builtContext). 265 FlagWithOutput("> ", remove_comment_output) 266 267 builtContext = remove_comment_output 268 } 269 270 if proptools.Bool(m.properties.Fc_sort) { 271 rule.Temporary(builtContext) 272 273 sorted_output := android.PathForModuleGen(ctx, ctx.ModuleName()+"_sorted") 274 275 rule.Command(). 276 Tool(ctx.Config().HostToolPath(ctx, "fc_sort")). 277 FlagWithInput("-i ", builtContext). 278 FlagWithOutput("-o ", sorted_output) 279 280 builtContext = sorted_output 281 } 282 283 ret := android.PathForModuleGen(ctx, m.stem()) 284 rule.Temporary(builtContext) 285 rule.Command().Text("cp").Input(builtContext).Output(ret) 286 287 rule.DeleteTemporaryFiles() 288 rule.Build("selinux_contexts", "building contexts: "+m.Name()) 289 290 return ret 291} 292 293func (m *selinuxContextsModule) buildFileContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 294 if m.properties.Fc_sort == nil { 295 m.properties.Fc_sort = proptools.BoolPtr(true) 296 } 297 298 rule := android.NewRuleBuilder(pctx, ctx) 299 300 if ctx.Config().FlattenApex() { 301 for _, path := range android.PathsForModuleSrc(ctx, m.fileContextsProperties.Flatten_apex.Srcs) { 302 out := android.PathForModuleGen(ctx, "flattened_apex", path.Rel()) 303 apex_path := "/system/apex/" + strings.Replace( 304 strings.TrimSuffix(path.Base(), "-file_contexts"), 305 ".", "\\\\.", -1) 306 307 rule.Command(). 308 Text("awk '/object_r/{printf(\""+apex_path+"%s\\n\",$0)}'"). 309 Input(path). 310 FlagWithOutput("> ", out) 311 312 inputs = append(inputs, out) 313 } 314 } 315 316 rule.Build(m.Name(), "flattened_apex_file_contexts") 317 return m.buildGeneralContexts(ctx, inputs) 318} 319 320func fileFactory() android.Module { 321 m := newModule() 322 m.build = m.buildFileContexts 323 return m 324} 325 326func (m *selinuxContextsModule) buildHwServiceContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 327 if m.properties.Remove_comment == nil { 328 m.properties.Remove_comment = proptools.BoolPtr(true) 329 } 330 331 return m.buildGeneralContexts(ctx, inputs) 332} 333 334func (m *selinuxContextsModule) checkVendorPropertyNamespace(ctx android.ModuleContext, inputs android.Paths) android.Paths { 335 shippingApiLevel := ctx.DeviceConfig().ShippingApiLevel() 336 ApiLevelR := android.ApiLevelOrPanic(ctx, "R") 337 338 rule := android.NewRuleBuilder(pctx, ctx) 339 340 // This list is from vts_treble_sys_prop_test. 341 allowedPropertyPrefixes := []string{ 342 "ctl.odm.", 343 "ctl.vendor.", 344 "ctl.start$odm.", 345 "ctl.start$vendor.", 346 "ctl.stop$odm.", 347 "ctl.stop$vendor.", 348 "init.svc.odm.", 349 "init.svc.vendor.", 350 "ro.boot.", 351 "ro.hardware.", 352 "ro.odm.", 353 "ro.vendor.", 354 "odm.", 355 "persist.odm.", 356 "persist.vendor.", 357 "vendor.", 358 } 359 360 // persist.camera is also allowed for devices launching with R or eariler 361 if shippingApiLevel.LessThanOrEqualTo(ApiLevelR) { 362 allowedPropertyPrefixes = append(allowedPropertyPrefixes, "persist.camera.") 363 } 364 365 var allowedContextPrefixes []string 366 367 if shippingApiLevel.GreaterThanOrEqualTo(ApiLevelR) { 368 // This list is from vts_treble_sys_prop_test. 369 allowedContextPrefixes = []string{ 370 "vendor_", 371 "odm_", 372 } 373 } 374 375 var ret android.Paths 376 for _, input := range inputs { 377 cmd := rule.Command(). 378 BuiltTool("check_prop_prefix"). 379 FlagWithInput("--property-contexts ", input). 380 FlagForEachArg("--allowed-property-prefix ", proptools.ShellEscapeList(allowedPropertyPrefixes)). // contains shell special character '$' 381 FlagForEachArg("--allowed-context-prefix ", allowedContextPrefixes) 382 383 if !ctx.DeviceConfig().BuildBrokenVendorPropertyNamespace() { 384 cmd.Flag("--strict") 385 } 386 387 out := android.PathForModuleGen(ctx, "namespace_checked").Join(ctx, input.String()) 388 rule.Command().Text("cp -f").Input(input).Output(out) 389 ret = append(ret, out) 390 } 391 rule.Build("check_namespace", "checking namespace of "+ctx.ModuleName()) 392 return ret 393} 394 395func (m *selinuxContextsModule) buildPropertyContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 396 // vendor/odm properties are enforced for devices launching with Android Q or later. So, if 397 // vendor/odm, make sure that only vendor/odm properties exist. 398 shippingApiLevel := ctx.DeviceConfig().ShippingApiLevel() 399 ApiLevelQ := android.ApiLevelOrPanic(ctx, "Q") 400 if (ctx.SocSpecific() || ctx.DeviceSpecific()) && shippingApiLevel.GreaterThanOrEqualTo(ApiLevelQ) { 401 inputs = m.checkVendorPropertyNamespace(ctx, inputs) 402 } 403 404 builtCtxFile := m.buildGeneralContexts(ctx, inputs) 405 406 var apiFiles android.Paths 407 ctx.VisitDirectDepsWithTag(syspropLibraryDepTag, func(c android.Module) { 408 i, ok := c.(interface{ CurrentSyspropApiFile() android.OptionalPath }) 409 if !ok { 410 panic(fmt.Errorf("unknown dependency %q for %q", ctx.OtherModuleName(c), ctx.ModuleName())) 411 } 412 if api := i.CurrentSyspropApiFile(); api.Valid() { 413 apiFiles = append(apiFiles, api.Path()) 414 } 415 }) 416 417 // check compatibility with sysprop_library 418 if len(apiFiles) > 0 { 419 out := android.PathForModuleGen(ctx, ctx.ModuleName()+"_api_checked") 420 rule := android.NewRuleBuilder(pctx, ctx) 421 422 msg := `\n******************************\n` + 423 `API of sysprop_library doesn't match with property_contexts\n` + 424 `Please fix the breakage and rebuild.\n` + 425 `******************************\n` 426 427 rule.Command(). 428 Text("( "). 429 BuiltTool("sysprop_type_checker"). 430 FlagForEachInput("--api ", apiFiles). 431 FlagWithInput("--context ", builtCtxFile). 432 Text(" || ( echo").Flag("-e"). 433 Flag(`"` + msg + `"`). 434 Text("; exit 38) )") 435 436 rule.Command().Text("cp -f").Input(builtCtxFile).Output(out) 437 rule.Build("property_contexts_check_api", "checking API: "+m.Name()) 438 builtCtxFile = out 439 } 440 441 return builtCtxFile 442} 443 444func (m *selinuxContextsModule) buildSeappContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 445 neverallowFile := android.PathForModuleGen(ctx, "neverallow") 446 ret := android.PathForModuleGen(ctx, m.stem()) 447 448 rule := android.NewRuleBuilder(pctx, ctx) 449 rule.Command().Text("(grep"). 450 Flag("-ihe"). 451 Text("'^neverallow'"). 452 Inputs(android.PathsForModuleSrc(ctx, m.seappProperties.Neverallow_files)). 453 Text(os.DevNull). // to make grep happy even when Neverallow_files is empty 454 Text(">"). 455 Output(neverallowFile). 456 Text("|| true)") // to make ninja happy even when result is empty 457 458 rule.Temporary(neverallowFile) 459 rule.Command().BuiltTool("checkseapp"). 460 FlagWithInput("-p ", android.PathForModuleSrc(ctx, proptools.String(m.seappProperties.Sepolicy))). 461 FlagWithOutput("-o ", ret). 462 Inputs(inputs). 463 Input(neverallowFile) 464 465 rule.Build("seapp_contexts", "Building seapp_contexts: "+m.Name()) 466 return ret 467} 468 469func hwServiceFactory() android.Module { 470 m := newModule() 471 m.build = m.buildHwServiceContexts 472 return m 473} 474 475func propertyFactory() android.Module { 476 m := newModule() 477 m.build = m.buildPropertyContexts 478 m.deps = m.propertyContextsDeps 479 return m 480} 481 482func serviceFactory() android.Module { 483 m := newModule() 484 m.build = m.buildGeneralContexts 485 return m 486} 487 488func keystoreKeyFactory() android.Module { 489 m := newModule() 490 m.build = m.buildGeneralContexts 491 return m 492} 493 494func seappFactory() android.Module { 495 m := newModule() 496 m.build = m.buildSeappContexts 497 return m 498} 499 500func vndServiceFactory() android.Module { 501 m := newModule() 502 m.build = m.buildGeneralContexts 503 android.AddLoadHook(m, func(ctx android.LoadHookContext) { 504 if !ctx.SocSpecific() { 505 ctx.ModuleErrorf(m.Name(), "must set vendor: true") 506 return 507 } 508 }) 509 return m 510} 511 512var _ android.OutputFileProducer = (*selinuxContextsModule)(nil) 513 514// Implements android.OutputFileProducer 515func (m *selinuxContextsModule) OutputFiles(tag string) (android.Paths, error) { 516 if tag == "" { 517 return []android.Path{m.outputPath}, nil 518 } 519 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 520} 521 522type contextsTestProperties struct { 523 // Contexts files to be tested. 524 Srcs []string `android:"path"` 525 526 // Precompiled sepolicy binary to be tesed together. 527 Sepolicy *string `android:"path"` 528} 529 530type contextsTestModule struct { 531 android.ModuleBase 532 533 // Name of the test tool. "checkfc" or "property_info_checker" 534 tool string 535 536 // Additional flags to be passed to the tool. 537 flags []string 538 539 properties contextsTestProperties 540 testTimestamp android.ModuleOutPath 541} 542 543// checkfc parses a context file and checks for syntax errors. 544// If -s is specified, the service backend is used to verify binder services. 545// If -l is specified, the service backend is used to verify hwbinder services. 546// Otherwise, context_file is assumed to be a file_contexts file 547// If -e is specified, then the context_file is allowed to be empty. 548 549// file_contexts_test tests given file_contexts files with checkfc. 550func fileContextsTestFactory() android.Module { 551 m := &contextsTestModule{tool: "checkfc" /* no flags: file_contexts file check */} 552 m.AddProperties(&m.properties) 553 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 554 return m 555} 556 557// property_contexts_test tests given property_contexts files with property_info_checker. 558func propertyContextsTestFactory() android.Module { 559 m := &contextsTestModule{tool: "property_info_checker"} 560 m.AddProperties(&m.properties) 561 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 562 return m 563} 564 565// hwservice_contexts_test tests given hwservice_contexts files with checkfc. 566func hwserviceContextsTestFactory() android.Module { 567 m := &contextsTestModule{tool: "checkfc", flags: []string{"-e" /* allow empty */, "-l" /* hwbinder services */}} 568 m.AddProperties(&m.properties) 569 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 570 return m 571} 572 573// service_contexts_test tests given service_contexts files with checkfc. 574func serviceContextsTestFactory() android.Module { 575 // checkfc -s: service_contexts test 576 m := &contextsTestModule{tool: "checkfc", flags: []string{"-s" /* binder services */}} 577 m.AddProperties(&m.properties) 578 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 579 return m 580} 581 582// vndservice_contexts_test tests given vndservice_contexts files with checkfc. 583func vndServiceContextsTestFactory() android.Module { 584 m := &contextsTestModule{tool: "checkfc", flags: []string{"-e" /* allow empty */, "-v" /* vnd service */}} 585 m.AddProperties(&m.properties) 586 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 587 return m 588} 589 590func (m *contextsTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 591 tool := m.tool 592 if tool != "checkfc" && tool != "property_info_checker" { 593 panic(fmt.Errorf("%q: unknown tool name: %q", ctx.ModuleName(), tool)) 594 } 595 596 if len(m.properties.Srcs) == 0 { 597 ctx.PropertyErrorf("srcs", "can't be empty") 598 return 599 } 600 601 if proptools.String(m.properties.Sepolicy) == "" { 602 ctx.PropertyErrorf("sepolicy", "can't be empty") 603 return 604 } 605 606 srcs := android.PathsForModuleSrc(ctx, m.properties.Srcs) 607 sepolicy := android.PathForModuleSrc(ctx, proptools.String(m.properties.Sepolicy)) 608 609 rule := android.NewRuleBuilder(pctx, ctx) 610 rule.Command().BuiltTool(tool). 611 Flags(m.flags). 612 Input(sepolicy). 613 Inputs(srcs) 614 615 m.testTimestamp = android.PathForModuleOut(ctx, "timestamp") 616 rule.Command().Text("touch").Output(m.testTimestamp) 617 rule.Build("contexts_test", "running contexts test: "+ctx.ModuleName()) 618} 619 620func (m *contextsTestModule) AndroidMkEntries() []android.AndroidMkEntries { 621 return []android.AndroidMkEntries{android.AndroidMkEntries{ 622 Class: "FAKE", 623 // OutputFile is needed, even though BUILD_PHONY_PACKAGE doesn't use it. 624 // Without OutputFile this module won't be exported to Makefile. 625 OutputFile: android.OptionalPathForPath(m.testTimestamp), 626 Include: "$(BUILD_PHONY_PACKAGE)", 627 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 628 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 629 entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", m.testTimestamp.String()) 630 }, 631 }, 632 }} 633} 634 635// contextsTestModule implements ImageInterface to be able to include recovery_available contexts 636// modules as its sources. 637func (m *contextsTestModule) ImageMutatorBegin(ctx android.BaseModuleContext) { 638} 639 640func (m *contextsTestModule) CoreVariantNeeded(ctx android.BaseModuleContext) bool { 641 return true 642} 643 644func (m *contextsTestModule) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 645 return false 646} 647 648func (m *contextsTestModule) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 649 return false 650} 651 652func (m *contextsTestModule) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 653 return false 654} 655 656func (m *contextsTestModule) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { 657 return false 658} 659 660func (m *contextsTestModule) ExtraImageVariations(ctx android.BaseModuleContext) []string { 661 return nil 662} 663 664func (m *contextsTestModule) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) { 665} 666 667var _ android.ImageInterface = (*contextsTestModule)(nil) 668