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 newlineFile := android.PathForModuleGen(ctx, "newline") 251 252 rule.Command().Text("echo").FlagWithOutput("> ", newlineFile) 253 rule.Temporary(newlineFile) 254 255 var inputsWithNewline android.Paths 256 for _, input := range inputs { 257 inputsWithNewline = append(inputsWithNewline, input, newlineFile) 258 } 259 260 rule.Command(). 261 Tool(ctx.Config().PrebuiltBuildTool(ctx, "m4")). 262 Text("--fatal-warnings -s"). 263 FlagForEachArg("-D", ctx.DeviceConfig().SepolicyM4Defs()). 264 Inputs(inputsWithNewline). 265 FlagWithOutput("> ", builtContext) 266 267 if proptools.Bool(m.properties.Remove_comment) { 268 rule.Temporary(builtContext) 269 270 remove_comment_output := android.PathForModuleGen(ctx, ctx.ModuleName()+"_remove_comment") 271 272 rule.Command(). 273 Text("sed -e 's/#.*$//' -e '/^$/d'"). 274 Input(builtContext). 275 FlagWithOutput("> ", remove_comment_output) 276 277 builtContext = remove_comment_output 278 } 279 280 if proptools.Bool(m.properties.Fc_sort) { 281 rule.Temporary(builtContext) 282 283 sorted_output := android.PathForModuleGen(ctx, ctx.ModuleName()+"_sorted") 284 285 rule.Command(). 286 Tool(ctx.Config().HostToolPath(ctx, "fc_sort")). 287 FlagWithInput("-i ", builtContext). 288 FlagWithOutput("-o ", sorted_output) 289 290 builtContext = sorted_output 291 } 292 293 ret := android.PathForModuleGen(ctx, m.stem()) 294 rule.Temporary(builtContext) 295 rule.Command().Text("cp").Input(builtContext).Output(ret) 296 297 rule.DeleteTemporaryFiles() 298 rule.Build("selinux_contexts", "building contexts: "+m.Name()) 299 300 return ret 301} 302 303func (m *selinuxContextsModule) buildFileContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 304 if m.properties.Fc_sort == nil { 305 m.properties.Fc_sort = proptools.BoolPtr(true) 306 } 307 308 rule := android.NewRuleBuilder(pctx, ctx) 309 310 if ctx.Config().FlattenApex() { 311 for _, path := range android.PathsForModuleSrc(ctx, m.fileContextsProperties.Flatten_apex.Srcs) { 312 out := android.PathForModuleGen(ctx, "flattened_apex", path.Rel()) 313 apex_path := "/system/apex/" + strings.Replace( 314 strings.TrimSuffix(path.Base(), "-file_contexts"), 315 ".", "\\\\.", -1) 316 317 rule.Command(). 318 Text("awk '/object_r/{printf(\""+apex_path+"%s\\n\",$0)}'"). 319 Input(path). 320 FlagWithOutput("> ", out) 321 322 inputs = append(inputs, out) 323 } 324 } 325 326 rule.Build(m.Name(), "flattened_apex_file_contexts") 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, inputs android.Paths) android.Paths { 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 var ret android.Paths 386 for _, input := range inputs { 387 cmd := rule.Command(). 388 BuiltTool("check_prop_prefix"). 389 FlagWithInput("--property-contexts ", input). 390 FlagForEachArg("--allowed-property-prefix ", proptools.ShellEscapeList(allowedPropertyPrefixes)). // contains shell special character '$' 391 FlagForEachArg("--allowed-context-prefix ", allowedContextPrefixes) 392 393 if !ctx.DeviceConfig().BuildBrokenVendorPropertyNamespace() { 394 cmd.Flag("--strict") 395 } 396 397 out := android.PathForModuleGen(ctx, "namespace_checked").Join(ctx, input.String()) 398 rule.Command().Text("cp -f").Input(input).Output(out) 399 ret = append(ret, out) 400 } 401 rule.Build("check_namespace", "checking namespace of "+ctx.ModuleName()) 402 return ret 403} 404 405func (m *selinuxContextsModule) buildPropertyContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 406 // vendor/odm properties are enforced for devices launching with Android Q or later. So, if 407 // vendor/odm, make sure that only vendor/odm properties exist. 408 shippingApiLevel := ctx.DeviceConfig().ShippingApiLevel() 409 ApiLevelQ := android.ApiLevelOrPanic(ctx, "Q") 410 if (ctx.SocSpecific() || ctx.DeviceSpecific()) && shippingApiLevel.GreaterThanOrEqualTo(ApiLevelQ) { 411 inputs = m.checkVendorPropertyNamespace(ctx, inputs) 412 } 413 414 builtCtxFile := m.buildGeneralContexts(ctx, inputs) 415 416 var apiFiles android.Paths 417 ctx.VisitDirectDepsWithTag(syspropLibraryDepTag, func(c android.Module) { 418 i, ok := c.(interface{ CurrentSyspropApiFile() android.OptionalPath }) 419 if !ok { 420 panic(fmt.Errorf("unknown dependency %q for %q", ctx.OtherModuleName(c), ctx.ModuleName())) 421 } 422 if api := i.CurrentSyspropApiFile(); api.Valid() { 423 apiFiles = append(apiFiles, api.Path()) 424 } 425 }) 426 427 // check compatibility with sysprop_library 428 if len(apiFiles) > 0 { 429 out := android.PathForModuleGen(ctx, ctx.ModuleName()+"_api_checked") 430 rule := android.NewRuleBuilder(pctx, ctx) 431 432 msg := `\n******************************\n` + 433 `API of sysprop_library doesn't match with property_contexts\n` + 434 `Please fix the breakage and rebuild.\n` + 435 `******************************\n` 436 437 rule.Command(). 438 Text("( "). 439 BuiltTool("sysprop_type_checker"). 440 FlagForEachInput("--api ", apiFiles). 441 FlagWithInput("--context ", builtCtxFile). 442 Text(" || ( echo").Flag("-e"). 443 Flag(`"` + msg + `"`). 444 Text("; exit 38) )") 445 446 rule.Command().Text("cp -f").Input(builtCtxFile).Output(out) 447 rule.Build("property_contexts_check_api", "checking API: "+m.Name()) 448 builtCtxFile = out 449 } 450 451 return builtCtxFile 452} 453 454func (m *selinuxContextsModule) buildSeappContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 455 neverallowFile := android.PathForModuleGen(ctx, "neverallow") 456 ret := android.PathForModuleGen(ctx, m.stem()) 457 458 rule := android.NewRuleBuilder(pctx, ctx) 459 rule.Command().Text("(grep"). 460 Flag("-ihe"). 461 Text("'^neverallow'"). 462 Inputs(android.PathsForModuleSrc(ctx, m.seappProperties.Neverallow_files)). 463 Text(os.DevNull). // to make grep happy even when Neverallow_files is empty 464 Text(">"). 465 Output(neverallowFile). 466 Text("|| true)") // to make ninja happy even when result is empty 467 468 rule.Temporary(neverallowFile) 469 rule.Command().BuiltTool("checkseapp"). 470 FlagWithInput("-p ", android.PathForModuleSrc(ctx, proptools.String(m.seappProperties.Sepolicy))). 471 FlagWithOutput("-o ", ret). 472 Inputs(inputs). 473 Input(neverallowFile) 474 475 rule.Build("seapp_contexts", "Building seapp_contexts: "+m.Name()) 476 return ret 477} 478 479func hwServiceFactory() android.Module { 480 m := newModule() 481 m.build = m.buildServiceContexts 482 return m 483} 484 485func propertyFactory() android.Module { 486 m := newModule() 487 m.build = m.buildPropertyContexts 488 m.deps = m.propertyContextsDeps 489 return m 490} 491 492func serviceFactory() android.Module { 493 m := newModule() 494 m.build = m.buildServiceContexts 495 return m 496} 497 498func keystoreKeyFactory() android.Module { 499 m := newModule() 500 m.build = m.buildGeneralContexts 501 return m 502} 503 504func seappFactory() android.Module { 505 m := newModule() 506 m.build = m.buildSeappContexts 507 return m 508} 509 510func vndServiceFactory() android.Module { 511 m := newModule() 512 m.build = m.buildGeneralContexts 513 android.AddLoadHook(m, func(ctx android.LoadHookContext) { 514 if !ctx.SocSpecific() { 515 ctx.ModuleErrorf(m.Name(), "must set vendor: true") 516 return 517 } 518 }) 519 return m 520} 521 522var _ android.OutputFileProducer = (*selinuxContextsModule)(nil) 523 524// Implements android.OutputFileProducer 525func (m *selinuxContextsModule) OutputFiles(tag string) (android.Paths, error) { 526 if tag == "" { 527 return []android.Path{m.outputPath}, nil 528 } 529 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 530} 531 532type contextsTestProperties struct { 533 // Contexts files to be tested. 534 Srcs []string `android:"path"` 535 536 // Precompiled sepolicy binary to be tesed together. 537 Sepolicy *string `android:"path"` 538} 539 540type contextsTestModule struct { 541 android.ModuleBase 542 543 // Name of the test tool. "checkfc" or "property_info_checker" 544 tool string 545 546 // Additional flags to be passed to the tool. 547 flags []string 548 549 properties contextsTestProperties 550 testTimestamp android.ModuleOutPath 551} 552 553// checkfc parses a context file and checks for syntax errors. 554// If -s is specified, the service backend is used to verify binder services. 555// If -l is specified, the service backend is used to verify hwbinder services. 556// Otherwise, context_file is assumed to be a file_contexts file 557// If -e is specified, then the context_file is allowed to be empty. 558 559// file_contexts_test tests given file_contexts files with checkfc. 560func fileContextsTestFactory() android.Module { 561 m := &contextsTestModule{tool: "checkfc" /* no flags: file_contexts file check */} 562 m.AddProperties(&m.properties) 563 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 564 return m 565} 566 567// property_contexts_test tests given property_contexts files with property_info_checker. 568func propertyContextsTestFactory() android.Module { 569 m := &contextsTestModule{tool: "property_info_checker"} 570 m.AddProperties(&m.properties) 571 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 572 return m 573} 574 575// hwservice_contexts_test tests given hwservice_contexts files with checkfc. 576func hwserviceContextsTestFactory() android.Module { 577 m := &contextsTestModule{tool: "checkfc", flags: []string{"-e" /* allow empty */, "-l" /* hwbinder services */}} 578 m.AddProperties(&m.properties) 579 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 580 return m 581} 582 583// service_contexts_test tests given service_contexts files with checkfc. 584func serviceContextsTestFactory() android.Module { 585 // checkfc -s: service_contexts test 586 m := &contextsTestModule{tool: "checkfc", flags: []string{"-s" /* binder services */}} 587 m.AddProperties(&m.properties) 588 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 589 return m 590} 591 592// vndservice_contexts_test tests given vndservice_contexts files with checkfc. 593func vndServiceContextsTestFactory() android.Module { 594 m := &contextsTestModule{tool: "checkfc", flags: []string{"-e" /* allow empty */, "-v" /* vnd service */}} 595 m.AddProperties(&m.properties) 596 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 597 return m 598} 599 600func (m *contextsTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 601 tool := m.tool 602 if tool != "checkfc" && tool != "property_info_checker" { 603 panic(fmt.Errorf("%q: unknown tool name: %q", ctx.ModuleName(), tool)) 604 } 605 606 if len(m.properties.Srcs) == 0 { 607 ctx.PropertyErrorf("srcs", "can't be empty") 608 return 609 } 610 611 if proptools.String(m.properties.Sepolicy) == "" { 612 ctx.PropertyErrorf("sepolicy", "can't be empty") 613 return 614 } 615 616 srcs := android.PathsForModuleSrc(ctx, m.properties.Srcs) 617 sepolicy := android.PathForModuleSrc(ctx, proptools.String(m.properties.Sepolicy)) 618 619 rule := android.NewRuleBuilder(pctx, ctx) 620 rule.Command().BuiltTool(tool). 621 Flags(m.flags). 622 Input(sepolicy). 623 Inputs(srcs) 624 625 m.testTimestamp = android.PathForModuleOut(ctx, "timestamp") 626 rule.Command().Text("touch").Output(m.testTimestamp) 627 rule.Build("contexts_test", "running contexts test: "+ctx.ModuleName()) 628} 629 630func (m *contextsTestModule) AndroidMkEntries() []android.AndroidMkEntries { 631 return []android.AndroidMkEntries{android.AndroidMkEntries{ 632 Class: "FAKE", 633 // OutputFile is needed, even though BUILD_PHONY_PACKAGE doesn't use it. 634 // Without OutputFile this module won't be exported to Makefile. 635 OutputFile: android.OptionalPathForPath(m.testTimestamp), 636 Include: "$(BUILD_PHONY_PACKAGE)", 637 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 638 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 639 entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", m.testTimestamp.String()) 640 }, 641 }, 642 }} 643} 644 645// contextsTestModule implements ImageInterface to be able to include recovery_available contexts 646// modules as its sources. 647func (m *contextsTestModule) ImageMutatorBegin(ctx android.BaseModuleContext) { 648} 649 650func (m *contextsTestModule) CoreVariantNeeded(ctx android.BaseModuleContext) bool { 651 return true 652} 653 654func (m *contextsTestModule) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 655 return false 656} 657 658func (m *contextsTestModule) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 659 return false 660} 661 662func (m *contextsTestModule) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 663 return false 664} 665 666func (m *contextsTestModule) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { 667 return false 668} 669 670func (m *contextsTestModule) ExtraImageVariations(ctx android.BaseModuleContext) []string { 671 return nil 672} 673 674func (m *contextsTestModule) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) { 675} 676 677var _ android.ImageInterface = (*contextsTestModule)(nil) 678