1// Copyright 2019 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package java 16 17import ( 18 "fmt" 19 20 "android/soong/android" 21) 22 23func init() { 24 android.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory) 25 android.RegisterSingletonType("hiddenapi_index", hiddenAPIIndexSingletonFactory) 26 android.RegisterModuleType("hiddenapi_flags", hiddenAPIFlagsFactory) 27} 28 29type hiddenAPISingletonPathsStruct struct { 30 flags android.OutputPath 31 index android.OutputPath 32 metadata android.OutputPath 33 stubFlags android.OutputPath 34} 35 36var hiddenAPISingletonPathsKey = android.NewOnceKey("hiddenAPISingletonPathsKey") 37 38// hiddenAPISingletonPaths creates all the paths for singleton files the first time it is called, which may be 39// from a ModuleContext that needs to reference a file that will be created by a singleton rule that hasn't 40// yet been created. 41func hiddenAPISingletonPaths(ctx android.PathContext) hiddenAPISingletonPathsStruct { 42 return ctx.Config().Once(hiddenAPISingletonPathsKey, func() interface{} { 43 return hiddenAPISingletonPathsStruct{ 44 flags: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-flags.csv"), 45 index: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-index.csv"), 46 metadata: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-greylist.csv"), 47 stubFlags: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-stub-flags.txt"), 48 } 49 }).(hiddenAPISingletonPathsStruct) 50} 51 52func hiddenAPISingletonFactory() android.Singleton { 53 return &hiddenAPISingleton{} 54} 55 56type hiddenAPISingleton struct { 57 flags, metadata android.Path 58} 59 60// hiddenAPI singleton rules 61func (h *hiddenAPISingleton) GenerateBuildActions(ctx android.SingletonContext) { 62 // Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true 63 if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { 64 return 65 } 66 67 stubFlagsRule(ctx) 68 69 // These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them. 70 if ctx.Config().FrameworksBaseDirExists(ctx) { 71 h.flags = flagsRule(ctx) 72 h.metadata = metadataRule(ctx) 73 } else { 74 h.flags = emptyFlagsRule(ctx) 75 } 76} 77 78// Export paths to Make. INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/. 79// Both paths are used to call dist-for-goals. 80func (h *hiddenAPISingleton) MakeVars(ctx android.MakeVarsContext) { 81 if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { 82 return 83 } 84 85 ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", h.flags.String()) 86 87 if h.metadata != nil { 88 ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA", h.metadata.String()) 89 } 90} 91 92// stubFlagsRule creates the rule to build hiddenapi-stub-flags.txt out of dex jars from stub modules and boot image 93// modules. 94func stubFlagsRule(ctx android.SingletonContext) { 95 // Public API stubs 96 publicStubModules := []string{ 97 "android_stubs_current", 98 } 99 100 // Add the android.test.base to the set of stubs only if the android.test.base module is on 101 // the boot jars list as the runtime will only enforce hiddenapi access against modules on 102 // that list. 103 if inList("android.test.base", ctx.Config().BootJars()) && !ctx.Config().UnbundledBuildUsePrebuiltSdks() { 104 publicStubModules = append(publicStubModules, "android.test.base.stubs") 105 } 106 107 // System API stubs 108 systemStubModules := []string{ 109 "android_system_stubs_current", 110 } 111 112 // Test API stubs 113 testStubModules := []string{ 114 "android_test_stubs_current", 115 } 116 117 // Core Platform API stubs 118 corePlatformStubModules := []string{ 119 "core.platform.api.stubs", 120 } 121 122 // Allow products to define their own stubs for custom product jars that apps can use. 123 publicStubModules = append(publicStubModules, ctx.Config().ProductHiddenAPIStubs()...) 124 systemStubModules = append(systemStubModules, ctx.Config().ProductHiddenAPIStubsSystem()...) 125 testStubModules = append(testStubModules, ctx.Config().ProductHiddenAPIStubsTest()...) 126 if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") { 127 publicStubModules = append(publicStubModules, "jacoco-stubs") 128 } 129 130 publicStubPaths := make(android.Paths, len(publicStubModules)) 131 systemStubPaths := make(android.Paths, len(systemStubModules)) 132 testStubPaths := make(android.Paths, len(testStubModules)) 133 corePlatformStubPaths := make(android.Paths, len(corePlatformStubModules)) 134 135 moduleListToPathList := map[*[]string]android.Paths{ 136 &publicStubModules: publicStubPaths, 137 &systemStubModules: systemStubPaths, 138 &testStubModules: testStubPaths, 139 &corePlatformStubModules: corePlatformStubPaths, 140 } 141 142 var bootDexJars android.Paths 143 144 ctx.VisitAllModules(func(module android.Module) { 145 // Collect dex jar paths for the modules listed above. 146 if j, ok := module.(Dependency); ok { 147 name := ctx.ModuleName(module) 148 for moduleList, pathList := range moduleListToPathList { 149 if i := android.IndexList(name, *moduleList); i != -1 { 150 pathList[i] = j.DexJar() 151 } 152 } 153 } 154 155 // Collect dex jar paths for modules that had hiddenapi encode called on them. 156 if h, ok := module.(hiddenAPIIntf); ok { 157 if jar := h.bootDexJar(); jar != nil { 158 // For a java lib included in an APEX, only take the one built for 159 // the platform variant, and skip the variants for APEXes. 160 // Otherwise, the hiddenapi tool will complain about duplicated classes 161 if a, ok := module.(android.ApexModule); ok { 162 if android.InAnyApex(module.Name()) && !a.IsForPlatform() { 163 return 164 } 165 } 166 bootDexJars = append(bootDexJars, jar) 167 } 168 } 169 }) 170 171 var missingDeps []string 172 // Ensure all modules were converted to paths 173 for moduleList, pathList := range moduleListToPathList { 174 for i := range pathList { 175 if pathList[i] == nil { 176 pathList[i] = android.PathForOutput(ctx, "missing") 177 if ctx.Config().AllowMissingDependencies() { 178 missingDeps = append(missingDeps, (*moduleList)[i]) 179 } else { 180 ctx.Errorf("failed to find dex jar path for module %q", 181 (*moduleList)[i]) 182 } 183 } 184 } 185 } 186 187 // Singleton rule which applies hiddenapi on all boot class path dex files. 188 rule := android.NewRuleBuilder() 189 190 outputPath := hiddenAPISingletonPaths(ctx).stubFlags 191 tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp") 192 193 rule.MissingDeps(missingDeps) 194 195 rule.Command(). 196 Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")). 197 Text("list"). 198 FlagForEachInput("--boot-dex=", bootDexJars). 199 FlagWithInputList("--public-stub-classpath=", publicStubPaths, ":"). 200 FlagWithInputList("--system-stub-classpath=", systemStubPaths, ":"). 201 FlagWithInputList("--test-stub-classpath=", testStubPaths, ":"). 202 FlagWithInputList("--core-platform-stub-classpath=", corePlatformStubPaths, ":"). 203 FlagWithOutput("--out-api-flags=", tempPath) 204 205 commitChangeForRestat(rule, tempPath, outputPath) 206 207 rule.Build(pctx, ctx, "hiddenAPIStubFlagsFile", "hiddenapi stub flags") 208} 209 210func moduleForGreyListRemovedApis(ctx android.SingletonContext, module android.Module) bool { 211 switch ctx.ModuleName(module) { 212 case "api-stubs-docs", "system-api-stubs-docs", "android.car-stubs-docs", "android.car-system-stubs-docs": 213 return true 214 default: 215 return false 216 } 217} 218 219// flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and 220// the greylists. 221func flagsRule(ctx android.SingletonContext) android.Path { 222 var flagsCSV android.Paths 223 var greylistRemovedApis android.Paths 224 225 ctx.VisitAllModules(func(module android.Module) { 226 if h, ok := module.(hiddenAPIIntf); ok { 227 if csv := h.flagsCSV(); csv != nil { 228 flagsCSV = append(flagsCSV, csv) 229 } 230 } else if ds, ok := module.(*Droidstubs); ok { 231 // Track @removed public and system APIs via corresponding droidstubs targets. 232 // These APIs are not present in the stubs, however, we have to keep allowing access 233 // to them at runtime. 234 if moduleForGreyListRemovedApis(ctx, module) { 235 greylistRemovedApis = append(greylistRemovedApis, ds.removedDexApiFile) 236 } 237 } 238 }) 239 240 combinedRemovedApis := android.PathForOutput(ctx, "hiddenapi", "combined-removed-dex.txt") 241 ctx.Build(pctx, android.BuildParams{ 242 Rule: android.Cat, 243 Inputs: greylistRemovedApis, 244 Output: combinedRemovedApis, 245 Description: "Combine removed apis for " + combinedRemovedApis.String(), 246 }) 247 248 rule := android.NewRuleBuilder() 249 250 outputPath := hiddenAPISingletonPaths(ctx).flags 251 tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp") 252 253 stubFlags := hiddenAPISingletonPaths(ctx).stubFlags 254 255 rule.Command(). 256 Tool(android.PathForSource(ctx, "frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py")). 257 FlagWithInput("--csv ", stubFlags). 258 Inputs(flagsCSV). 259 FlagWithInput("--greylist ", 260 android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist.txt")). 261 FlagWithInput("--greylist-ignore-conflicts ", combinedRemovedApis). 262 FlagWithInput("--greylist-max-q ", 263 android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-q.txt")). 264 FlagWithInput("--greylist-max-p ", 265 android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-p.txt")). 266 FlagWithInput("--greylist-max-o-ignore-conflicts ", 267 android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-o.txt")). 268 FlagWithInput("--blacklist ", 269 android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blacklist.txt")). 270 FlagWithInput("--greylist-packages ", 271 android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-packages.txt")). 272 FlagWithOutput("--output ", tempPath) 273 274 commitChangeForRestat(rule, tempPath, outputPath) 275 276 rule.Build(pctx, ctx, "hiddenAPIFlagsFile", "hiddenapi flags") 277 278 return outputPath 279} 280 281// emptyFlagsRule creates a rule to build an empty hiddenapi-flags.csv, which is needed by master-art-host builds that 282// have a partial manifest without frameworks/base but still need to build a boot image. 283func emptyFlagsRule(ctx android.SingletonContext) android.Path { 284 rule := android.NewRuleBuilder() 285 286 outputPath := hiddenAPISingletonPaths(ctx).flags 287 288 rule.Command().Text("rm").Flag("-f").Output(outputPath) 289 rule.Command().Text("touch").Output(outputPath) 290 291 rule.Build(pctx, ctx, "emptyHiddenAPIFlagsFile", "empty hiddenapi flags") 292 293 return outputPath 294} 295 296// metadataRule creates a rule to build hiddenapi-greylist.csv out of the metadata.csv files generated for boot image 297// modules. 298func metadataRule(ctx android.SingletonContext) android.Path { 299 var metadataCSV android.Paths 300 301 ctx.VisitAllModules(func(module android.Module) { 302 if h, ok := module.(hiddenAPIIntf); ok { 303 if csv := h.metadataCSV(); csv != nil { 304 metadataCSV = append(metadataCSV, csv) 305 } 306 } 307 }) 308 309 rule := android.NewRuleBuilder() 310 311 outputPath := hiddenAPISingletonPaths(ctx).metadata 312 313 rule.Command(). 314 BuiltTool(ctx, "merge_csv"). 315 FlagWithOutput("--output=", outputPath). 316 Inputs(metadataCSV) 317 318 rule.Build(pctx, ctx, "hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata") 319 320 return outputPath 321} 322 323// commitChangeForRestat adds a command to a rule that updates outputPath from tempPath if they are different. It 324// also marks the rule as restat and marks the tempPath as a temporary file that should not be considered an output of 325// the rule. 326func commitChangeForRestat(rule *android.RuleBuilder, tempPath, outputPath android.WritablePath) { 327 rule.Restat() 328 rule.Temporary(tempPath) 329 rule.Command(). 330 Text("("). 331 Text("if"). 332 Text("cmp -s").Input(tempPath).Output(outputPath).Text(";"). 333 Text("then"). 334 Text("rm").Input(tempPath).Text(";"). 335 Text("else"). 336 Text("mv").Input(tempPath).Output(outputPath).Text(";"). 337 Text("fi"). 338 Text(")") 339} 340 341type hiddenAPIFlagsProperties struct { 342 // name of the file into which the flags will be copied. 343 Filename *string 344} 345 346type hiddenAPIFlags struct { 347 android.ModuleBase 348 349 properties hiddenAPIFlagsProperties 350 351 outputFilePath android.OutputPath 352} 353 354func (h *hiddenAPIFlags) GenerateAndroidBuildActions(ctx android.ModuleContext) { 355 filename := String(h.properties.Filename) 356 357 inputPath := hiddenAPISingletonPaths(ctx).flags 358 h.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath 359 360 // This ensures that outputFilePath has the correct name for others to 361 // use, as the source file may have a different name. 362 ctx.Build(pctx, android.BuildParams{ 363 Rule: android.Cp, 364 Output: h.outputFilePath, 365 Input: inputPath, 366 }) 367} 368 369func (h *hiddenAPIFlags) OutputFiles(tag string) (android.Paths, error) { 370 switch tag { 371 case "": 372 return android.Paths{h.outputFilePath}, nil 373 default: 374 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 375 } 376} 377 378// hiddenapi-flags provides access to the hiddenapi-flags.csv file generated during the build. 379func hiddenAPIFlagsFactory() android.Module { 380 module := &hiddenAPIFlags{} 381 module.AddProperties(&module.properties) 382 android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon) 383 return module 384} 385 386func hiddenAPIIndexSingletonFactory() android.Singleton { 387 return &hiddenAPIIndexSingleton{} 388} 389 390type hiddenAPIIndexSingleton struct { 391 index android.Path 392} 393 394func (h *hiddenAPIIndexSingleton) GenerateBuildActions(ctx android.SingletonContext) { 395 // Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true 396 if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { 397 return 398 } 399 400 indexes := android.Paths{} 401 ctx.VisitAllModules(func(module android.Module) { 402 if h, ok := module.(hiddenAPIIntf); ok { 403 if h.indexCSV() != nil { 404 indexes = append(indexes, h.indexCSV()) 405 } 406 } 407 }) 408 409 rule := android.NewRuleBuilder() 410 rule.Command(). 411 BuiltTool(ctx, "merge_csv"). 412 FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties"). 413 FlagWithOutput("--output=", hiddenAPISingletonPaths(ctx).index). 414 Inputs(indexes) 415 rule.Build(pctx, ctx, "singleton-merged-hiddenapi-index", "Singleton merged Hidden API index") 416 417 h.index = hiddenAPISingletonPaths(ctx).index 418} 419 420func (h *hiddenAPIIndexSingleton) MakeVars(ctx android.MakeVarsContext) { 421 if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { 422 return 423 } 424 425 ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_INDEX", h.index.String()) 426} 427