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 apex 16 17import ( 18 "encoding/json" 19 "fmt" 20 "path/filepath" 21 "runtime" 22 "sort" 23 "strconv" 24 "strings" 25 26 "android/soong/android" 27 "android/soong/java" 28 29 "github.com/google/blueprint" 30 "github.com/google/blueprint/proptools" 31) 32 33var ( 34 pctx = android.NewPackageContext("android/apex") 35) 36 37func init() { 38 pctx.Import("android/soong/android") 39 pctx.Import("android/soong/cc/config") 40 pctx.Import("android/soong/java") 41 pctx.HostBinToolVariable("apexer", "apexer") 42 pctx.HostBinToolVariable("apexer_with_DCLA_preprocessing", "apexer_with_DCLA_preprocessing") 43 pctx.HostBinToolVariable("apexer_with_trim_preprocessing", "apexer_with_trim_preprocessing") 44 45 // ART minimal builds (using the master-art manifest) do not have the "frameworks/base" 46 // projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead. 47 hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) { 48 pctx.VariableFunc(name, func(ctx android.PackageVarContext) string { 49 if !ctx.Config().FrameworksBaseDirExists(ctx) { 50 return filepath.Join(prebuiltDir, runtime.GOOS, "bin", tool) 51 } else { 52 return ctx.Config().HostToolPath(ctx, tool).String() 53 } 54 }) 55 } 56 hostBinToolVariableWithPrebuilt("aapt2", "prebuilts/sdk/tools", "aapt2") 57 pctx.HostBinToolVariable("avbtool", "avbtool") 58 pctx.HostBinToolVariable("e2fsdroid", "e2fsdroid") 59 pctx.HostBinToolVariable("merge_zips", "merge_zips") 60 pctx.HostBinToolVariable("mke2fs", "mke2fs") 61 pctx.HostBinToolVariable("resize2fs", "resize2fs") 62 pctx.HostBinToolVariable("sefcontext_compile", "sefcontext_compile") 63 pctx.HostBinToolVariable("soong_zip", "soong_zip") 64 pctx.HostBinToolVariable("zip2zip", "zip2zip") 65 pctx.HostBinToolVariable("zipalign", "zipalign") 66 pctx.HostBinToolVariable("jsonmodify", "jsonmodify") 67 pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest") 68 pctx.HostBinToolVariable("extract_apks", "extract_apks") 69 pctx.HostBinToolVariable("make_f2fs", "make_f2fs") 70 pctx.HostBinToolVariable("sload_f2fs", "sload_f2fs") 71 pctx.HostBinToolVariable("make_erofs", "make_erofs") 72 pctx.HostBinToolVariable("apex_compression_tool", "apex_compression_tool") 73 pctx.HostBinToolVariable("dexdeps", "dexdeps") 74 pctx.HostBinToolVariable("apex_sepolicy_tests", "apex_sepolicy_tests") 75 pctx.HostBinToolVariable("deapexer", "deapexer") 76 pctx.HostBinToolVariable("debugfs_static", "debugfs_static") 77 pctx.SourcePathVariable("genNdkUsedbyApexPath", "build/soong/scripts/gen_ndk_usedby_apex.sh") 78} 79 80var ( 81 apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{ 82 Command: `rm -f $out && ${jsonmodify} $in ` + 83 `-a provideNativeLibs ${provideNativeLibs} ` + 84 `-a requireNativeLibs ${requireNativeLibs} ` + 85 `-se version 0 ${default_version} ` + 86 `${opt} ` + 87 `-o $out`, 88 CommandDeps: []string{"${jsonmodify}"}, 89 Description: "prepare ${out}", 90 }, "provideNativeLibs", "requireNativeLibs", "default_version", "opt") 91 92 stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{ 93 Command: `rm -f $out && ${conv_apex_manifest} strip $in -o $out`, 94 CommandDeps: []string{"${conv_apex_manifest}"}, 95 Description: "strip ${in}=>${out}", 96 }) 97 98 pbApexManifestRule = pctx.StaticRule("pbApexManifestRule", blueprint.RuleParams{ 99 Command: `rm -f $out && ${conv_apex_manifest} proto $in -o $out`, 100 CommandDeps: []string{"${conv_apex_manifest}"}, 101 Description: "convert ${in}=>${out}", 102 }) 103 104 // TODO(b/113233103): make sure that file_contexts is as expected, i.e., validate 105 // against the binary policy using sefcontext_compiler -p <policy>. 106 107 // TODO(b/114327326): automate the generation of file_contexts 108 apexRule = pctx.StaticRule("apexRule", blueprint.RuleParams{ 109 Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` + 110 `(. ${out}.copy_commands) && ` + 111 `APEXER_TOOL_PATH=${tool_path} ` + 112 `${apexer} --force --manifest ${manifest} ` + 113 `--file_contexts ${file_contexts} ` + 114 `--canned_fs_config ${canned_fs_config} ` + 115 `--include_build_info ` + 116 `--payload_type image ` + 117 `--key ${key} ${opt_flags} ${image_dir} ${out} `, 118 CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}", 119 "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}", "${sload_f2fs}", "${make_erofs}", 120 "${soong_zip}", "${zipalign}", "${aapt2}", "prebuilts/sdk/current/public/android.jar"}, 121 Rspfile: "${out}.copy_commands", 122 RspfileContent: "${copy_commands}", 123 Description: "APEX ${image_dir} => ${out}", 124 }, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", 125 "opt_flags", "manifest") 126 127 DCLAApexRule = pctx.StaticRule("DCLAApexRule", blueprint.RuleParams{ 128 Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` + 129 `(. ${out}.copy_commands) && ` + 130 `APEXER_TOOL_PATH=${tool_path} ` + 131 `${apexer_with_DCLA_preprocessing} ` + 132 `--apexer ${apexer} ` + 133 `--canned_fs_config ${canned_fs_config} ` + 134 `${image_dir} ` + 135 `${out} ` + 136 `-- ` + 137 `--include_build_info ` + 138 `--force ` + 139 `--payload_type image ` + 140 `--key ${key} ` + 141 `--file_contexts ${file_contexts} ` + 142 `--manifest ${manifest} ` + 143 `${opt_flags} `, 144 CommandDeps: []string{"${apexer_with_DCLA_preprocessing}", "${apexer}", "${avbtool}", "${e2fsdroid}", 145 "${merge_zips}", "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}", 146 "${sload_f2fs}", "${make_erofs}", "${soong_zip}", "${zipalign}", "${aapt2}", 147 "prebuilts/sdk/current/public/android.jar"}, 148 Rspfile: "${out}.copy_commands", 149 RspfileContent: "${copy_commands}", 150 Description: "APEX ${image_dir} => ${out}", 151 }, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", 152 "opt_flags", "manifest", "is_DCLA") 153 154 TrimmedApexRule = pctx.StaticRule("TrimmedApexRule", blueprint.RuleParams{ 155 Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` + 156 `(. ${out}.copy_commands) && ` + 157 `APEXER_TOOL_PATH=${tool_path} ` + 158 `${apexer_with_trim_preprocessing} ` + 159 `--apexer ${apexer} ` + 160 `--canned_fs_config ${canned_fs_config} ` + 161 `--manifest ${manifest} ` + 162 `--libs_to_trim ${libs_to_trim} ` + 163 `${image_dir} ` + 164 `${out} ` + 165 `-- ` + 166 `--include_build_info ` + 167 `--force ` + 168 `--payload_type image ` + 169 `--key ${key} ` + 170 `--file_contexts ${file_contexts} ` + 171 `${opt_flags} `, 172 CommandDeps: []string{"${apexer_with_trim_preprocessing}", "${apexer}", "${avbtool}", "${e2fsdroid}", 173 "${merge_zips}", "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}", 174 "${sload_f2fs}", "${make_erofs}", "${soong_zip}", "${zipalign}", "${aapt2}", 175 "prebuilts/sdk/current/public/android.jar"}, 176 Rspfile: "${out}.copy_commands", 177 RspfileContent: "${copy_commands}", 178 Description: "APEX ${image_dir} => ${out}", 179 }, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", 180 "opt_flags", "manifest", "libs_to_trim") 181 182 zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{ 183 Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` + 184 `(. ${out}.copy_commands) && ` + 185 `APEXER_TOOL_PATH=${tool_path} ` + 186 `${apexer} --force --manifest ${manifest} ` + 187 `--payload_type zip ` + 188 `${image_dir} ${out} `, 189 CommandDeps: []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"}, 190 Rspfile: "${out}.copy_commands", 191 RspfileContent: "${copy_commands}", 192 Description: "ZipAPEX ${image_dir} => ${out}", 193 }, "tool_path", "image_dir", "copy_commands", "manifest") 194 195 apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule", 196 blueprint.RuleParams{ 197 Command: `${aapt2} convert --output-format proto $in -o $out`, 198 CommandDeps: []string{"${aapt2}"}, 199 }) 200 201 apexBundleRule = pctx.StaticRule("apexBundleRule", blueprint.RuleParams{ 202 Command: `${zip2zip} -i $in -o $out.base ` + 203 `apex_payload.img:apex/${abi}.img ` + 204 `apex_build_info.pb:apex/${abi}.build_info.pb ` + 205 `apex_manifest.json:root/apex_manifest.json ` + 206 `apex_manifest.pb:root/apex_manifest.pb ` + 207 `AndroidManifest.xml:manifest/AndroidManifest.xml ` + 208 `assets/NOTICE.html.gz:assets/NOTICE.html.gz &&` + 209 `${soong_zip} -o $out.config -C $$(dirname ${config}) -f ${config} && ` + 210 `${merge_zips} $out $out.base $out.config`, 211 CommandDeps: []string{"${zip2zip}", "${soong_zip}", "${merge_zips}"}, 212 Description: "app bundle", 213 }, "abi", "config") 214 215 diffApexContentRule = pctx.StaticRule("diffApexContentRule", blueprint.RuleParams{ 216 Command: `diff --unchanged-group-format='' \` + 217 `--changed-group-format='%<' \` + 218 `${image_content_file} ${allowed_files_file} || (` + 219 `echo -e "New unexpected files were added to ${apex_module_name}." ` + 220 ` "To fix the build run following command:" && ` + 221 `echo "system/apex/tools/update_allowed_list.sh ${allowed_files_file} ${image_content_file}" && ` + 222 `exit 1); touch ${out}`, 223 Description: "Diff ${image_content_file} and ${allowed_files_file}", 224 }, "image_content_file", "allowed_files_file", "apex_module_name") 225 226 generateAPIsUsedbyApexRule = pctx.StaticRule("generateAPIsUsedbyApexRule", blueprint.RuleParams{ 227 Command: "$genNdkUsedbyApexPath ${image_dir} ${readelf} ${out}", 228 CommandDeps: []string{"${genNdkUsedbyApexPath}"}, 229 Description: "Generate symbol list used by Apex", 230 }, "image_dir", "readelf") 231 232 apexSepolicyTestsRule = pctx.StaticRule("apexSepolicyTestsRule", blueprint.RuleParams{ 233 Command: `${deapexer} --debugfs_path ${debugfs_static} list -Z ${in} > ${out}.fc` + 234 `&& ${apex_sepolicy_tests} -f ${out}.fc && touch ${out}`, 235 CommandDeps: []string{"${apex_sepolicy_tests}", "${deapexer}", "${debugfs_static}"}, 236 Description: "run apex_sepolicy_tests", 237 }) 238) 239 240// buildManifest creates buile rules to modify the input apex_manifest.json to add information 241// gathered by the build system such as provided/required native libraries. Two output files having 242// different formats are generated. a.manifestJsonOut is JSON format for Q devices, and 243// a.manifest.PbOut is protobuf format for R+ devices. 244// TODO(jiyong): make this to return paths instead of directly storing the paths to apexBundle 245func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, requireNativeLibs []string) { 246 src := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json")) 247 248 // Put dependency({provide|require}NativeLibs) in apex_manifest.json 249 provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs) 250 requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs)) 251 252 // VNDK APEX name is determined at runtime, so update "name" in apex_manifest 253 optCommands := []string{} 254 if a.vndkApex { 255 apexName := vndkApexNamePrefix + a.vndkVersion(ctx.DeviceConfig()) 256 optCommands = append(optCommands, "-v name "+apexName) 257 } 258 259 // Collect jniLibs. Notice that a.filesInfo is already sorted 260 var jniLibs []string 261 for _, fi := range a.filesInfo { 262 if fi.isJniLib && !android.InList(fi.stem(), jniLibs) { 263 jniLibs = append(jniLibs, fi.stem()) 264 } 265 } 266 if len(jniLibs) > 0 { 267 optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " ")) 268 } 269 270 if android.InList(":vndk", requireNativeLibs) { 271 if _, vndkVersion := a.getImageVariationPair(ctx.DeviceConfig()); vndkVersion != "" { 272 optCommands = append(optCommands, "-v vndkVersion "+vndkVersion) 273 } 274 } 275 276 manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json") 277 defaultVersion := android.DefaultUpdatableModuleVersion 278 if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" { 279 defaultVersion = override 280 } 281 ctx.Build(pctx, android.BuildParams{ 282 Rule: apexManifestRule, 283 Input: src, 284 Output: manifestJsonFullOut, 285 Args: map[string]string{ 286 "provideNativeLibs": strings.Join(provideNativeLibs, " "), 287 "requireNativeLibs": strings.Join(requireNativeLibs, " "), 288 "default_version": defaultVersion, 289 "opt": strings.Join(optCommands, " "), 290 }, 291 }) 292 293 // b/143654022 Q apexd can't understand newly added keys in apex_manifest.json prepare 294 // stripped-down version so that APEX modules built from R+ can be installed to Q 295 minSdkVersion := a.minSdkVersion(ctx) 296 if minSdkVersion.EqualTo(android.SdkVersion_Android10) { 297 a.manifestJsonOut = android.PathForModuleOut(ctx, "apex_manifest.json") 298 ctx.Build(pctx, android.BuildParams{ 299 Rule: stripApexManifestRule, 300 Input: manifestJsonFullOut, 301 Output: a.manifestJsonOut, 302 }) 303 } 304 305 // From R+, protobuf binary format (.pb) is the standard format for apex_manifest 306 a.manifestPbOut = android.PathForModuleOut(ctx, "apex_manifest.pb") 307 ctx.Build(pctx, android.BuildParams{ 308 Rule: pbApexManifestRule, 309 Input: manifestJsonFullOut, 310 Output: a.manifestPbOut, 311 }) 312} 313 314// buildFileContexts create build rules to append an entry for apex_manifest.pb to the file_contexts 315// file for this APEX which is either from /systme/sepolicy/apex/<apexname>-file_contexts or from 316// the file_contexts property of this APEX. This is to make sure that the manifest file is correctly 317// labeled as system_file. 318func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.OutputPath { 319 var fileContexts android.Path 320 var fileContextsDir string 321 if a.properties.File_contexts == nil { 322 fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts") 323 } else { 324 if m, t := android.SrcIsModuleWithTag(*a.properties.File_contexts); m != "" { 325 otherModule := android.GetModuleFromPathDep(ctx, m, t) 326 fileContextsDir = ctx.OtherModuleDir(otherModule) 327 } 328 fileContexts = android.PathForModuleSrc(ctx, *a.properties.File_contexts) 329 } 330 if fileContextsDir == "" { 331 fileContextsDir = filepath.Dir(fileContexts.String()) 332 } 333 fileContextsDir += string(filepath.Separator) 334 335 if a.Platform() { 336 if !strings.HasPrefix(fileContextsDir, "system/sepolicy/") { 337 ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but found in %q", fileContextsDir) 338 } 339 } 340 if !android.ExistentPathForSource(ctx, fileContexts.String()).Valid() { 341 ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", fileContexts.String()) 342 } 343 344 useFileContextsAsIs := proptools.Bool(a.properties.Use_file_contexts_as_is) 345 346 output := android.PathForModuleOut(ctx, "file_contexts") 347 rule := android.NewRuleBuilder(pctx, ctx) 348 349 switch a.properties.ApexType { 350 case imageApex: 351 // remove old file 352 rule.Command().Text("rm").FlagWithOutput("-f ", output) 353 // copy file_contexts 354 rule.Command().Text("cat").Input(fileContexts).Text(">>").Output(output) 355 // new line 356 rule.Command().Text("echo").Text(">>").Output(output) 357 if !useFileContextsAsIs { 358 // force-label /apex_manifest.pb and / as system_file so that apexd can read them 359 rule.Command().Text("echo").Flag("/apex_manifest\\\\.pb u:object_r:system_file:s0").Text(">>").Output(output) 360 rule.Command().Text("echo").Flag("/ u:object_r:system_file:s0").Text(">>").Output(output) 361 } 362 case flattenedApex: 363 // For flattened apexes, install path should be prepended. 364 // File_contexts file should be emiited to make via LOCAL_FILE_CONTEXTS 365 // so that it can be merged into file_contexts.bin 366 apexPath := android.InstallPathToOnDevicePath(ctx, a.installDir.Join(ctx, a.Name())) 367 apexPath = strings.ReplaceAll(apexPath, ".", `\\.`) 368 // remove old file 369 rule.Command().Text("rm").FlagWithOutput("-f ", output) 370 // copy file_contexts 371 rule.Command().Text("awk").Text(`'/object_r/{printf("` + apexPath + `%s\n", $0)}'`).Input(fileContexts).Text(">").Output(output) 372 // new line 373 rule.Command().Text("echo").Text(">>").Output(output) 374 if !useFileContextsAsIs { 375 // force-label /apex_manifest.pb and / as system_file so that apexd can read them 376 rule.Command().Text("echo").Flag(apexPath + `/apex_manifest\\.pb u:object_r:system_file:s0`).Text(">>").Output(output) 377 rule.Command().Text("echo").Flag(apexPath + "/ u:object_r:system_file:s0").Text(">>").Output(output) 378 } 379 default: 380 panic(fmt.Errorf("unsupported type %v", a.properties.ApexType)) 381 } 382 383 rule.Build("file_contexts."+a.Name(), "Generate file_contexts") 384 return output.OutputPath 385} 386 387// buildInstalledFilesFile creates a build rule for the installed-files.txt file where the list of 388// files included in this APEX is shown. The text file is dist'ed so that people can see what's 389// included in the APEX without actually downloading and extracting it. 390func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.OutputPath { 391 output := android.PathForModuleOut(ctx, "installed-files.txt") 392 rule := android.NewRuleBuilder(pctx, ctx) 393 rule.Command(). 394 Implicit(builtApex). 395 Text("(cd " + imageDir.String() + " ; "). 396 Text("find . \\( -type f -o -type l \\) -printf \"%s %p\\n\") "). 397 Text(" | sort -nr > "). 398 Output(output) 399 rule.Build("installed-files."+a.Name(), "Installed files") 400 return output.OutputPath 401} 402 403// buildBundleConfig creates a build rule for the bundle config file that will control the bundle 404// creation process. 405func (a *apexBundle) buildBundleConfig(ctx android.ModuleContext) android.OutputPath { 406 output := android.PathForModuleOut(ctx, "bundle_config.json") 407 408 type ApkConfig struct { 409 Package_name string `json:"package_name"` 410 Apk_path string `json:"path"` 411 } 412 config := struct { 413 Compression struct { 414 Uncompressed_glob []string `json:"uncompressed_glob"` 415 } `json:"compression"` 416 Apex_config struct { 417 Apex_embedded_apk_config []ApkConfig `json:"apex_embedded_apk_config,omitempty"` 418 } `json:"apex_config,omitempty"` 419 }{} 420 421 config.Compression.Uncompressed_glob = []string{ 422 "apex_payload.img", 423 "apex_manifest.*", 424 } 425 426 // Collect the manifest names and paths of android apps if their manifest names are 427 // overridden. 428 for _, fi := range a.filesInfo { 429 if fi.class != app && fi.class != appSet { 430 continue 431 } 432 packageName := fi.overriddenPackageName 433 if packageName != "" { 434 config.Apex_config.Apex_embedded_apk_config = append( 435 config.Apex_config.Apex_embedded_apk_config, 436 ApkConfig{ 437 Package_name: packageName, 438 Apk_path: fi.path(), 439 }) 440 } 441 } 442 443 j, err := json.Marshal(config) 444 if err != nil { 445 panic(fmt.Errorf("error while marshalling to %q: %#v", output, err)) 446 } 447 448 android.WriteFileRule(ctx, output, string(j)) 449 450 return output.OutputPath 451} 452 453func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android.Path) android.Path { 454 return java.ManifestFixer(ctx, androidManifestFile, java.ManifestFixerParams{ 455 TestOnly: true, 456 }) 457} 458 459// buildUnflattendApex creates build rules to build an APEX using apexer. 460func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { 461 apexType := a.properties.ApexType 462 suffix := apexType.suffix() 463 apexName := a.BaseModuleName() 464 465 //////////////////////////////////////////////////////////////////////////////////////////// 466 // Step 1: copy built files to appropriate directories under the image directory 467 468 imageDir := android.PathForModuleOut(ctx, "image"+suffix) 469 470 installSymbolFiles := (!ctx.Config().KatiEnabled() || a.ExportedToMake()) && a.installable() 471 // We can't install symbol files when prebuilt is used. 472 if a.IsReplacedByPrebuilt() { 473 installSymbolFiles = false 474 } 475 476 // set of dependency module:location mappings 477 installMapSet := make(map[string]bool) 478 479 // TODO(jiyong): use the RuleBuilder 480 var copyCommands []string 481 var implicitInputs []android.Path 482 apexDir := android.PathForModuleInPartitionInstall(ctx, "apex", apexName) 483 for _, fi := range a.filesInfo { 484 destPath := imageDir.Join(ctx, fi.path()).String() 485 // Prepare the destination path 486 destPathDir := filepath.Dir(destPath) 487 if fi.class == appSet { 488 copyCommands = append(copyCommands, "rm -rf "+destPathDir) 489 } 490 copyCommands = append(copyCommands, "mkdir -p "+destPathDir) 491 492 installMapPath := fi.builtFile 493 494 // Copy the built file to the directory. But if the symlink optimization is turned 495 // on, place a symlink to the corresponding file in /system partition instead. 496 if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() { 497 pathOnDevice := filepath.Join("/", fi.partition, fi.path()) 498 copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath) 499 } else { 500 // Copy the file into APEX 501 copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath) 502 503 var installedPath android.InstallPath 504 if fi.class == appSet { 505 // In case of AppSet, we need to copy additional APKs as well. They 506 // are zipped. So we need to unzip them. 507 copyCommands = append(copyCommands, 508 fmt.Sprintf("unzip -qDD -d %s %s", destPathDir, 509 fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs().String())) 510 if installSymbolFiles { 511 installedPath = ctx.InstallFileWithExtraFilesZip(apexDir.Join(ctx, fi.installDir), 512 fi.stem(), fi.builtFile, fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs()) 513 } 514 } else { 515 if installSymbolFiles { 516 installedPath = ctx.InstallFile(apexDir.Join(ctx, fi.installDir), fi.stem(), fi.builtFile) 517 } 518 } 519 implicitInputs = append(implicitInputs, fi.builtFile) 520 521 // Create additional symlinks pointing the file inside the APEX (if any). Note that 522 // this is independent from the symlink optimization. 523 for _, symlinkPath := range fi.symlinkPaths() { 524 symlinkDest := imageDir.Join(ctx, symlinkPath).String() 525 copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest) 526 if installSymbolFiles { 527 ctx.InstallSymlink(apexDir.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath) 528 } 529 } 530 531 installMapPath = installedPath 532 } 533 534 // Copy the test files (if any) 535 for _, d := range fi.dataPaths { 536 // TODO(eakammer): This is now the third repetition of ~this logic for test paths, refactoring should be possible 537 relPath := d.SrcPath.Rel() 538 dataPath := d.SrcPath.String() 539 if !strings.HasSuffix(dataPath, relPath) { 540 panic(fmt.Errorf("path %q does not end with %q", dataPath, relPath)) 541 } 542 543 dataDest := imageDir.Join(ctx, fi.apexRelativePath(relPath), d.RelativeInstallPath).String() 544 545 copyCommands = append(copyCommands, "cp -f "+d.SrcPath.String()+" "+dataDest) 546 implicitInputs = append(implicitInputs, d.SrcPath) 547 } 548 549 installMapSet[installMapPath.String()+":"+fi.installDir+"/"+fi.builtFile.Base()] = true 550 } 551 implicitInputs = append(implicitInputs, a.manifestPbOut) 552 553 if len(installMapSet) > 0 { 554 var installs []string 555 installs = append(installs, android.SortedKeys(installMapSet)...) 556 a.SetLicenseInstallMap(installs) 557 } 558 559 //////////////////////////////////////////////////////////////////////////////////////////// 560 // Step 1.a: Write the list of files in this APEX to a txt file and compare it against 561 // the allowed list given via the allowed_files property. Build fails when the two lists 562 // differ. 563 // 564 // TODO(jiyong): consider removing this. Nobody other than com.android.apex.cts.shim.* seems 565 // to be using this at this moment. Furthermore, this looks very similar to what 566 // buildInstalledFilesFile does. At least, move this to somewhere else so that this doesn't 567 // hurt readability. 568 if a.overridableProperties.Allowed_files != nil { 569 // Build content.txt 570 var contentLines []string 571 imageContentFile := android.PathForModuleOut(ctx, "content.txt") 572 contentLines = append(contentLines, "./apex_manifest.pb") 573 minSdkVersion := a.minSdkVersion(ctx) 574 if minSdkVersion.EqualTo(android.SdkVersion_Android10) { 575 contentLines = append(contentLines, "./apex_manifest.json") 576 } 577 for _, fi := range a.filesInfo { 578 contentLines = append(contentLines, "./"+fi.path()) 579 } 580 sort.Strings(contentLines) 581 android.WriteFileRule(ctx, imageContentFile, strings.Join(contentLines, "\n")) 582 implicitInputs = append(implicitInputs, imageContentFile) 583 584 // Compare content.txt against allowed_files. 585 allowedFilesFile := android.PathForModuleSrc(ctx, proptools.String(a.overridableProperties.Allowed_files)) 586 phonyOutput := android.PathForModuleOut(ctx, a.Name()+"-diff-phony-output") 587 ctx.Build(pctx, android.BuildParams{ 588 Rule: diffApexContentRule, 589 Implicits: implicitInputs, 590 Output: phonyOutput, 591 Description: "diff apex image content", 592 Args: map[string]string{ 593 "allowed_files_file": allowedFilesFile.String(), 594 "image_content_file": imageContentFile.String(), 595 "apex_module_name": a.Name(), 596 }, 597 }) 598 implicitInputs = append(implicitInputs, phonyOutput) 599 } 600 601 unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned") 602 outHostBinDir := ctx.Config().HostToolPath(ctx, "").String() 603 prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin") 604 605 if apexType == imageApex { 606 607 //////////////////////////////////////////////////////////////////////////////////// 608 // Step 2: create canned_fs_config which encodes filemode,uid,gid of each files 609 // in this APEX. The file will be used by apexer in later steps. 610 cannedFsConfig := a.buildCannedFsConfig(ctx) 611 implicitInputs = append(implicitInputs, cannedFsConfig) 612 613 //////////////////////////////////////////////////////////////////////////////////// 614 // Step 3: Prepare option flags for apexer and invoke it to create an unsigned APEX. 615 // TODO(jiyong): use the RuleBuilder 616 optFlags := []string{} 617 618 fileContexts := a.buildFileContexts(ctx) 619 implicitInputs = append(implicitInputs, fileContexts) 620 621 implicitInputs = append(implicitInputs, a.privateKeyFile, a.publicKeyFile) 622 optFlags = append(optFlags, "--pubkey "+a.publicKeyFile.String()) 623 624 manifestPackageName := a.getOverrideManifestPackageName(ctx) 625 if manifestPackageName != "" { 626 optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName) 627 } 628 629 if a.properties.AndroidManifest != nil { 630 androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest)) 631 632 if a.testApex { 633 androidManifestFile = markManifestTestOnly(ctx, androidManifestFile) 634 } 635 636 implicitInputs = append(implicitInputs, androidManifestFile) 637 optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String()) 638 } else if a.testApex { 639 optFlags = append(optFlags, "--test_only") 640 } 641 642 // Determine target/min sdk version from the context 643 // TODO(jiyong): make this as a function 644 moduleMinSdkVersion := a.minSdkVersion(ctx) 645 minSdkVersion := moduleMinSdkVersion.String() 646 647 // bundletool doesn't understand what "current" is. We need to transform it to 648 // codename 649 if moduleMinSdkVersion.IsCurrent() || moduleMinSdkVersion.IsNone() { 650 minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String() 651 652 if java.UseApiFingerprint(ctx) { 653 minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String()) 654 implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx)) 655 } 656 } 657 // apex module doesn't have a concept of target_sdk_version, hence for the time 658 // being targetSdkVersion == default targetSdkVersion of the branch. 659 targetSdkVersion := strconv.Itoa(ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt()) 660 661 if java.UseApiFingerprint(ctx) { 662 targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String()) 663 implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx)) 664 } 665 optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion) 666 optFlags = append(optFlags, "--min_sdk_version "+minSdkVersion) 667 668 if a.overridableProperties.Logging_parent != "" { 669 optFlags = append(optFlags, "--logging_parent ", a.overridableProperties.Logging_parent) 670 } 671 672 // Create a NOTICE file, and embed it as an asset file in the APEX. 673 htmlGzNotice := android.PathForModuleOut(ctx, "NOTICE.html.gz") 674 android.BuildNoticeHtmlOutputFromLicenseMetadata( 675 ctx, htmlGzNotice, "", "", 676 []string{ 677 android.PathForModuleInstall(ctx).String() + "/", 678 android.PathForModuleInPartitionInstall(ctx, "apex").String() + "/", 679 }) 680 noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz") 681 builder := android.NewRuleBuilder(pctx, ctx) 682 builder.Command().Text("cp"). 683 Input(htmlGzNotice). 684 Output(noticeAssetPath) 685 builder.Build("notice_dir", "Building notice dir") 686 implicitInputs = append(implicitInputs, noticeAssetPath) 687 optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeAssetPath.String())) 688 689 // Apexes which are supposed to be installed in builtin dirs(/system, etc) 690 // don't need hashtree for activation. Therefore, by removing hashtree from 691 // apex bundle (filesystem image in it, to be specific), we can save storage. 692 needHashTree := moduleMinSdkVersion.LessThanOrEqualTo(android.SdkVersion_Android10) || 693 a.shouldGenerateHashtree() 694 if ctx.Config().ApexCompressionEnabled() && a.isCompressable() { 695 needHashTree = true 696 } 697 if !needHashTree { 698 optFlags = append(optFlags, "--no_hashtree") 699 } 700 701 if a.testOnlyShouldSkipPayloadSign() { 702 optFlags = append(optFlags, "--unsigned_payload") 703 } 704 705 if moduleMinSdkVersion == android.SdkVersion_Android10 { 706 implicitInputs = append(implicitInputs, a.manifestJsonOut) 707 optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String()) 708 } 709 710 optFlags = append(optFlags, "--payload_fs_type "+a.payloadFsType.string()) 711 712 if a.dynamic_common_lib_apex() { 713 ctx.Build(pctx, android.BuildParams{ 714 Rule: DCLAApexRule, 715 Implicits: implicitInputs, 716 Output: unsignedOutputFile, 717 Description: "apex (" + apexType.name() + ")", 718 Args: map[string]string{ 719 "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, 720 "image_dir": imageDir.String(), 721 "copy_commands": strings.Join(copyCommands, " && "), 722 "manifest": a.manifestPbOut.String(), 723 "file_contexts": fileContexts.String(), 724 "canned_fs_config": cannedFsConfig.String(), 725 "key": a.privateKeyFile.String(), 726 "opt_flags": strings.Join(optFlags, " "), 727 }, 728 }) 729 } else if ctx.Config().ApexTrimEnabled() && len(a.libs_to_trim(ctx)) > 0 { 730 ctx.Build(pctx, android.BuildParams{ 731 Rule: TrimmedApexRule, 732 Implicits: implicitInputs, 733 Output: unsignedOutputFile, 734 Description: "apex (" + apexType.name() + ")", 735 Args: map[string]string{ 736 "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, 737 "image_dir": imageDir.String(), 738 "copy_commands": strings.Join(copyCommands, " && "), 739 "manifest": a.manifestPbOut.String(), 740 "file_contexts": fileContexts.String(), 741 "canned_fs_config": cannedFsConfig.String(), 742 "key": a.privateKeyFile.String(), 743 "opt_flags": strings.Join(optFlags, " "), 744 "libs_to_trim": strings.Join(a.libs_to_trim(ctx), ","), 745 }, 746 }) 747 } else { 748 ctx.Build(pctx, android.BuildParams{ 749 Rule: apexRule, 750 Implicits: implicitInputs, 751 Output: unsignedOutputFile, 752 Description: "apex (" + apexType.name() + ")", 753 Args: map[string]string{ 754 "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, 755 "image_dir": imageDir.String(), 756 "copy_commands": strings.Join(copyCommands, " && "), 757 "manifest": a.manifestPbOut.String(), 758 "file_contexts": fileContexts.String(), 759 "canned_fs_config": cannedFsConfig.String(), 760 "key": a.privateKeyFile.String(), 761 "opt_flags": strings.Join(optFlags, " "), 762 }, 763 }) 764 } 765 766 // TODO(jiyong): make the two rules below as separate functions 767 apexProtoFile := android.PathForModuleOut(ctx, a.Name()+".pb"+suffix) 768 bundleModuleFile := android.PathForModuleOut(ctx, a.Name()+suffix+"-base.zip") 769 a.bundleModuleFile = bundleModuleFile 770 771 ctx.Build(pctx, android.BuildParams{ 772 Rule: apexProtoConvertRule, 773 Input: unsignedOutputFile, 774 Output: apexProtoFile, 775 Description: "apex proto convert", 776 }) 777 778 implicitInputs = append(implicitInputs, unsignedOutputFile) 779 780 // Run coverage analysis 781 apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.txt") 782 ctx.Build(pctx, android.BuildParams{ 783 Rule: generateAPIsUsedbyApexRule, 784 Implicits: implicitInputs, 785 Description: "coverage", 786 Output: apisUsedbyOutputFile, 787 Args: map[string]string{ 788 "image_dir": imageDir.String(), 789 "readelf": "${config.ClangBin}/llvm-readelf", 790 }, 791 }) 792 a.nativeApisUsedByModuleFile = apisUsedbyOutputFile 793 794 var nativeLibNames []string 795 for _, f := range a.filesInfo { 796 if f.class == nativeSharedLib { 797 nativeLibNames = append(nativeLibNames, f.stem()) 798 } 799 } 800 apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt") 801 rule := android.NewRuleBuilder(pctx, ctx) 802 rule.Command(). 803 Tool(android.PathForSource(ctx, "build/soong/scripts/gen_ndk_backedby_apex.sh")). 804 Output(apisBackedbyOutputFile). 805 Flags(nativeLibNames) 806 rule.Build("ndk_backedby_list", "Generate API libraries backed by Apex") 807 a.nativeApisBackedByModuleFile = apisBackedbyOutputFile 808 809 var javaLibOrApkPath []android.Path 810 for _, f := range a.filesInfo { 811 if f.class == javaSharedLib || f.class == app { 812 javaLibOrApkPath = append(javaLibOrApkPath, f.builtFile) 813 } 814 } 815 javaApiUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.xml") 816 javaUsedByRule := android.NewRuleBuilder(pctx, ctx) 817 javaUsedByRule.Command(). 818 Tool(android.PathForSource(ctx, "build/soong/scripts/gen_java_usedby_apex.sh")). 819 BuiltTool("dexdeps"). 820 Output(javaApiUsedbyOutputFile). 821 Inputs(javaLibOrApkPath) 822 javaUsedByRule.Build("java_usedby_list", "Generate Java APIs used by Apex") 823 a.javaApisUsedByModuleFile = javaApiUsedbyOutputFile 824 825 bundleConfig := a.buildBundleConfig(ctx) 826 827 var abis []string 828 for _, target := range ctx.MultiTargets() { 829 if len(target.Arch.Abi) > 0 { 830 abis = append(abis, target.Arch.Abi[0]) 831 } 832 } 833 834 abis = android.FirstUniqueStrings(abis) 835 836 ctx.Build(pctx, android.BuildParams{ 837 Rule: apexBundleRule, 838 Input: apexProtoFile, 839 Implicit: bundleConfig, 840 Output: a.bundleModuleFile, 841 Description: "apex bundle module", 842 Args: map[string]string{ 843 "abi": strings.Join(abis, "."), 844 "config": bundleConfig.String(), 845 }, 846 }) 847 } else { // zipApex 848 ctx.Build(pctx, android.BuildParams{ 849 Rule: zipApexRule, 850 Implicits: implicitInputs, 851 Output: unsignedOutputFile, 852 Description: "apex (" + apexType.name() + ")", 853 Args: map[string]string{ 854 "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, 855 "image_dir": imageDir.String(), 856 "copy_commands": strings.Join(copyCommands, " && "), 857 "manifest": a.manifestPbOut.String(), 858 }, 859 }) 860 } 861 862 //////////////////////////////////////////////////////////////////////////////////// 863 // Step 4: Sign the APEX using signapk 864 signedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix) 865 866 pem, key := a.getCertificateAndPrivateKey(ctx) 867 rule := java.Signapk 868 args := map[string]string{ 869 "certificates": pem.String() + " " + key.String(), 870 "flags": "-a 4096 --align-file-size", //alignment 871 } 872 implicits := android.Paths{pem, key} 873 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") { 874 rule = java.SignapkRE 875 args["implicits"] = strings.Join(implicits.Strings(), ",") 876 args["outCommaList"] = signedOutputFile.String() 877 } 878 var validations android.Paths 879 if suffix == imageApexSuffix { 880 validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile.OutputPath)) 881 } 882 ctx.Build(pctx, android.BuildParams{ 883 Rule: rule, 884 Description: "signapk", 885 Output: signedOutputFile, 886 Input: unsignedOutputFile, 887 Implicits: implicits, 888 Args: args, 889 Validations: validations, 890 }) 891 if suffix == imageApexSuffix { 892 a.outputApexFile = signedOutputFile 893 } 894 a.outputFile = signedOutputFile 895 896 if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() { 897 ctx.PropertyErrorf("test_only_force_compression", "not available") 898 return 899 } 900 901 installSuffix := suffix 902 a.setCompression(ctx) 903 if a.isCompressed { 904 unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix+".unsigned") 905 906 compressRule := android.NewRuleBuilder(pctx, ctx) 907 compressRule.Command(). 908 Text("rm"). 909 FlagWithOutput("-f ", unsignedCompressedOutputFile) 910 compressRule.Command(). 911 BuiltTool("apex_compression_tool"). 912 Flag("compress"). 913 FlagWithArg("--apex_compression_tool ", outHostBinDir+":"+prebuiltSdkToolsBinDir). 914 FlagWithInput("--input ", signedOutputFile). 915 FlagWithOutput("--output ", unsignedCompressedOutputFile) 916 compressRule.Build("compressRule", "Generate unsigned compressed APEX file") 917 918 signedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix) 919 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") { 920 args["outCommaList"] = signedCompressedOutputFile.String() 921 } 922 ctx.Build(pctx, android.BuildParams{ 923 Rule: rule, 924 Description: "sign compressedApex", 925 Output: signedCompressedOutputFile, 926 Input: unsignedCompressedOutputFile, 927 Implicits: implicits, 928 Args: args, 929 }) 930 a.outputFile = signedCompressedOutputFile 931 installSuffix = imageCapexSuffix 932 } 933 934 if !a.installable() { 935 a.SkipInstall() 936 } 937 938 // Install to $OUT/soong/{target,host}/.../apex. 939 a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile, 940 a.compatSymlinks.Paths()...) 941 942 // installed-files.txt is dist'ed 943 a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir) 944} 945 946// buildFlattenedApex creates rules for a flattened APEX. Flattened APEX actually doesn't have a 947// single output file. It is a phony target for all the files under /system/apex/<name> directory. 948// This function creates the installation rules for the files. 949func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) { 950 bundleName := a.Name() 951 installedSymlinks := append(android.InstallPaths(nil), a.compatSymlinks...) 952 if a.installable() { 953 for _, fi := range a.filesInfo { 954 dir := filepath.Join("apex", bundleName, fi.installDir) 955 installDir := android.PathForModuleInstall(ctx, dir) 956 if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() { 957 pathOnDevice := filepath.Join("/", fi.partition, fi.path()) 958 installedSymlinks = append(installedSymlinks, 959 ctx.InstallAbsoluteSymlink(installDir, fi.stem(), pathOnDevice)) 960 } else { 961 if fi.class == appSet { 962 as := fi.module.(*java.AndroidAppSet) 963 ctx.InstallFileWithExtraFilesZip(installDir, as.BaseModuleName()+".apk", 964 as.OutputFile(), as.PackedAdditionalOutputs()) 965 } else { 966 target := ctx.InstallFile(installDir, fi.stem(), fi.builtFile) 967 for _, sym := range fi.symlinks { 968 installedSymlinks = append(installedSymlinks, 969 ctx.InstallSymlink(installDir, sym, target)) 970 } 971 } 972 } 973 } 974 975 // Create install rules for the files added in GenerateAndroidBuildActions after 976 // buildFlattenedApex is called. Add the links to system libs (if any) as dependencies 977 // of the apex_manifest.pb file since it is always present. 978 dir := filepath.Join("apex", bundleName) 979 installDir := android.PathForModuleInstall(ctx, dir) 980 ctx.InstallFile(installDir, "apex_manifest.pb", a.manifestPbOut, installedSymlinks.Paths()...) 981 ctx.InstallFile(installDir, "apex_pubkey", a.publicKeyFile) 982 } 983 984 a.fileContexts = a.buildFileContexts(ctx) 985 986 a.outputFile = android.PathForModuleInstall(ctx, "apex", bundleName) 987} 988 989// getCertificateAndPrivateKey retrieves the cert and the private key that will be used to sign 990// the zip container of this APEX. See the description of the 'certificate' property for how 991// the cert and the private key are found. 992func (a *apexBundle) getCertificateAndPrivateKey(ctx android.PathContext) (pem, key android.Path) { 993 if a.containerCertificateFile != nil { 994 return a.containerCertificateFile, a.containerPrivateKeyFile 995 } 996 997 cert := String(a.overridableProperties.Certificate) 998 if cert == "" { 999 return ctx.Config().DefaultAppCertificate(ctx) 1000 } 1001 1002 defaultDir := ctx.Config().DefaultAppCertificateDir(ctx) 1003 pem = defaultDir.Join(ctx, cert+".x509.pem") 1004 key = defaultDir.Join(ctx, cert+".pk8") 1005 return pem, key 1006} 1007 1008func (a *apexBundle) getOverrideManifestPackageName(ctx android.ModuleContext) string { 1009 // For VNDK APEXes, check "com.android.vndk" in PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES 1010 // to see if it should be overridden because their <apex name> is dynamically generated 1011 // according to its VNDK version. 1012 if a.vndkApex { 1013 overrideName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(vndkApexName) 1014 if overridden { 1015 return overrideName + ".v" + a.vndkVersion(ctx.DeviceConfig()) 1016 } 1017 return "" 1018 } 1019 if a.overridableProperties.Package_name != "" { 1020 return a.overridableProperties.Package_name 1021 } 1022 manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName()) 1023 if overridden { 1024 return manifestPackageName 1025 } 1026 return "" 1027} 1028 1029func (a *apexBundle) buildApexDependencyInfo(ctx android.ModuleContext) { 1030 if !a.primaryApexType { 1031 return 1032 } 1033 1034 if a.properties.IsCoverageVariant { 1035 // Otherwise, we will have duplicated rules for coverage and 1036 // non-coverage variants of the same APEX 1037 return 1038 } 1039 1040 if ctx.Host() { 1041 // No need to generate dependency info for host variant 1042 return 1043 } 1044 1045 depInfos := android.DepNameToDepInfoMap{} 1046 a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { 1047 if from.Name() == to.Name() { 1048 // This can happen for cc.reuseObjTag. We are not interested in tracking this. 1049 // As soon as the dependency graph crosses the APEX boundary, don't go further. 1050 return !externalDep 1051 } 1052 1053 // Skip dependencies that are only available to APEXes; they are developed with updatability 1054 // in mind and don't need manual approval. 1055 if to.(android.ApexModule).NotAvailableForPlatform() { 1056 return !externalDep 1057 } 1058 1059 depTag := ctx.OtherModuleDependencyTag(to) 1060 // Check to see if dependency been marked to skip the dependency check 1061 if skipDepCheck, ok := depTag.(android.SkipApexAllowedDependenciesCheck); ok && skipDepCheck.SkipApexAllowedDependenciesCheck() { 1062 return !externalDep 1063 } 1064 1065 if info, exists := depInfos[to.Name()]; exists { 1066 if !android.InList(from.Name(), info.From) { 1067 info.From = append(info.From, from.Name()) 1068 } 1069 info.IsExternal = info.IsExternal && externalDep 1070 depInfos[to.Name()] = info 1071 } else { 1072 toMinSdkVersion := "(no version)" 1073 if m, ok := to.(interface { 1074 MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel 1075 }); ok { 1076 if v := m.MinSdkVersion(ctx); !v.IsNone() { 1077 toMinSdkVersion = v.String() 1078 } 1079 } else if m, ok := to.(interface{ MinSdkVersion() string }); ok { 1080 // TODO(b/175678607) eliminate the use of MinSdkVersion returning 1081 // string 1082 if v := m.MinSdkVersion(); v != "" { 1083 toMinSdkVersion = v 1084 } 1085 } 1086 depInfos[to.Name()] = android.ApexModuleDepInfo{ 1087 To: to.Name(), 1088 From: []string{from.Name()}, 1089 IsExternal: externalDep, 1090 MinSdkVersion: toMinSdkVersion, 1091 } 1092 } 1093 1094 // As soon as the dependency graph crosses the APEX boundary, don't go further. 1095 return !externalDep 1096 }) 1097 1098 a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).String(), depInfos) 1099 1100 ctx.Build(pctx, android.BuildParams{ 1101 Rule: android.Phony, 1102 Output: android.PathForPhony(ctx, a.Name()+"-deps-info"), 1103 Inputs: []android.Path{ 1104 a.ApexBundleDepsInfo.FullListPath(), 1105 a.ApexBundleDepsInfo.FlatListPath(), 1106 }, 1107 }) 1108} 1109 1110func (a *apexBundle) buildLintReports(ctx android.ModuleContext) { 1111 depSetsBuilder := java.NewLintDepSetBuilder() 1112 for _, fi := range a.filesInfo { 1113 depSetsBuilder.Transitive(fi.lintDepSets) 1114 } 1115 1116 a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build()) 1117} 1118 1119func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.OutputPath { 1120 var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"} 1121 var executablePaths []string // this also includes dirs 1122 var appSetDirs []string 1123 appSetFiles := make(map[string]android.Path) 1124 for _, f := range a.filesInfo { 1125 pathInApex := f.path() 1126 if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") { 1127 executablePaths = append(executablePaths, pathInApex) 1128 for _, d := range f.dataPaths { 1129 readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.RelativeInstallPath, d.SrcPath.Rel())) 1130 } 1131 for _, s := range f.symlinks { 1132 executablePaths = append(executablePaths, filepath.Join(f.installDir, s)) 1133 } 1134 } else if f.class == appSet { 1135 // base APK 1136 readOnlyPaths = append(readOnlyPaths, pathInApex) 1137 // Additional APKs 1138 appSetDirs = append(appSetDirs, f.installDir) 1139 appSetFiles[f.installDir] = f.module.(*java.AndroidAppSet).PackedAdditionalOutputs() 1140 } else { 1141 readOnlyPaths = append(readOnlyPaths, pathInApex) 1142 } 1143 dir := f.installDir 1144 for !android.InList(dir, executablePaths) && dir != "" { 1145 executablePaths = append(executablePaths, dir) 1146 dir, _ = filepath.Split(dir) // move up to the parent 1147 if len(dir) > 0 { 1148 // remove trailing slash 1149 dir = dir[:len(dir)-1] 1150 } 1151 } 1152 } 1153 sort.Strings(readOnlyPaths) 1154 sort.Strings(executablePaths) 1155 sort.Strings(appSetDirs) 1156 1157 cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config") 1158 builder := android.NewRuleBuilder(pctx, ctx) 1159 cmd := builder.Command() 1160 cmd.Text("(") 1161 cmd.Text("echo '/ 1000 1000 0755';") 1162 for _, p := range readOnlyPaths { 1163 cmd.Textf("echo '/%s 1000 1000 0644';", p) 1164 } 1165 for _, p := range executablePaths { 1166 cmd.Textf("echo '/%s 0 2000 0755';", p) 1167 } 1168 for _, dir := range appSetDirs { 1169 cmd.Textf("echo '/%s 0 2000 0755';", dir) 1170 file := appSetFiles[dir] 1171 cmd.Text("zipinfo -1").Input(file).Textf(`| sed "s:\(.*\):/%s/\1 1000 1000 0644:";`, dir) 1172 } 1173 // Custom fs_config is "appended" to the last so that entries from the file are preferred 1174 // over default ones set above. 1175 if a.properties.Canned_fs_config != nil { 1176 cmd.Text("cat").Input(android.PathForModuleSrc(ctx, *a.properties.Canned_fs_config)) 1177 } 1178 cmd.Text(")").FlagWithOutput("> ", cannedFsConfig) 1179 builder.Build("generateFsConfig", fmt.Sprintf("Generating canned fs config for %s", a.BaseModuleName())) 1180 1181 return cannedFsConfig.OutputPath 1182} 1183 1184// Runs apex_sepolicy_tests 1185// 1186// $ deapexer list -Z {apex_file} > {file_contexts} 1187// $ apex_sepolicy_tests -f {file_contexts} 1188func runApexSepolicyTests(ctx android.ModuleContext, apexFile android.OutputPath) android.Path { 1189 timestamp := android.PathForModuleOut(ctx, "sepolicy_tests.timestamp") 1190 ctx.Build(pctx, android.BuildParams{ 1191 Rule: apexSepolicyTestsRule, 1192 Input: apexFile, 1193 Output: timestamp, 1194 }) 1195 return timestamp 1196} 1197