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" 21 "path/filepath" 22 "runtime" 23 "sort" 24 "strconv" 25 "strings" 26 27 "android/soong/android" 28 "android/soong/java" 29 30 "github.com/google/blueprint" 31 "github.com/google/blueprint/proptools" 32) 33 34var ( 35 pctx = android.NewPackageContext("android/apex") 36) 37 38func init() { 39 pctx.Import("android/soong/android") 40 pctx.Import("android/soong/java") 41 pctx.HostBinToolVariable("apexer", "apexer") 42 // ART minimal builds (using the master-art manifest) do not have the "frameworks/base" 43 // projects, and hence cannot built 'aapt2'. Use the SDK prebuilt instead. 44 hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) { 45 pctx.VariableFunc(name, func(ctx android.PackageVarContext) string { 46 if !ctx.Config().FrameworksBaseDirExists(ctx) { 47 return filepath.Join(prebuiltDir, runtime.GOOS, "bin", tool) 48 } else { 49 return ctx.Config().HostToolPath(ctx, tool).String() 50 } 51 }) 52 } 53 hostBinToolVariableWithPrebuilt("aapt2", "prebuilts/sdk/tools", "aapt2") 54 pctx.HostBinToolVariable("avbtool", "avbtool") 55 pctx.HostBinToolVariable("e2fsdroid", "e2fsdroid") 56 pctx.HostBinToolVariable("merge_zips", "merge_zips") 57 pctx.HostBinToolVariable("mke2fs", "mke2fs") 58 pctx.HostBinToolVariable("resize2fs", "resize2fs") 59 pctx.HostBinToolVariable("sefcontext_compile", "sefcontext_compile") 60 pctx.HostBinToolVariable("soong_zip", "soong_zip") 61 pctx.HostBinToolVariable("zip2zip", "zip2zip") 62 pctx.HostBinToolVariable("zipalign", "zipalign") 63 pctx.HostBinToolVariable("jsonmodify", "jsonmodify") 64 pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest") 65 pctx.HostBinToolVariable("extract_apks", "extract_apks") 66} 67 68var ( 69 // Create a canned fs config file where all files and directories are 70 // by default set to (uid/gid/mode) = (1000/1000/0644) 71 // TODO(b/113082813) make this configurable using config.fs syntax 72 generateFsConfig = pctx.StaticRule("generateFsConfig", blueprint.RuleParams{ 73 Command: `( echo '/ 1000 1000 0755' ` + 74 `&& for i in ${ro_paths}; do echo "/$$i 1000 1000 0644"; done ` + 75 `&& for i in ${exec_paths}; do echo "/$$i 0 2000 0755"; done ` + 76 `&& ( tr ' ' '\n' <${out}.apklist | for i in ${apk_paths}; do read apk; echo "/$$i 0 2000 0755"; zipinfo -1 $$apk | sed "s:\(.*\):/$$i/\1 1000 1000 0644:"; done ) ) > ${out}`, 77 Description: "fs_config ${out}", 78 Rspfile: "$out.apklist", 79 RspfileContent: "$in", 80 }, "ro_paths", "exec_paths", "apk_paths") 81 82 apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{ 83 Command: `rm -f $out && ${jsonmodify} $in ` + 84 `-a provideNativeLibs ${provideNativeLibs} ` + 85 `-a requireNativeLibs ${requireNativeLibs} ` + 86 `${opt} ` + 87 `-o $out`, 88 CommandDeps: []string{"${jsonmodify}"}, 89 Description: "prepare ${out}", 90 }, "provideNativeLibs", "requireNativeLibs", "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 sane, 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}", 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", "opt_flags", "manifest") 125 126 zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{ 127 Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` + 128 `(. ${out}.copy_commands) && ` + 129 `APEXER_TOOL_PATH=${tool_path} ` + 130 `${apexer} --force --manifest ${manifest} ` + 131 `--payload_type zip ` + 132 `${image_dir} ${out} `, 133 CommandDeps: []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"}, 134 Rspfile: "${out}.copy_commands", 135 RspfileContent: "${copy_commands}", 136 Description: "ZipAPEX ${image_dir} => ${out}", 137 }, "tool_path", "image_dir", "copy_commands", "manifest") 138 139 apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule", 140 blueprint.RuleParams{ 141 Command: `${aapt2} convert --output-format proto $in -o $out`, 142 CommandDeps: []string{"${aapt2}"}, 143 }) 144 145 apexBundleRule = pctx.StaticRule("apexBundleRule", blueprint.RuleParams{ 146 Command: `${zip2zip} -i $in -o $out.base ` + 147 `apex_payload.img:apex/${abi}.img ` + 148 `apex_build_info.pb:apex/${abi}.build_info.pb ` + 149 `apex_manifest.json:root/apex_manifest.json ` + 150 `apex_manifest.pb:root/apex_manifest.pb ` + 151 `AndroidManifest.xml:manifest/AndroidManifest.xml ` + 152 `assets/NOTICE.html.gz:assets/NOTICE.html.gz &&` + 153 `${soong_zip} -o $out.config -C $$(dirname ${config}) -f ${config} && ` + 154 `${merge_zips} $out $out.base $out.config`, 155 CommandDeps: []string{"${zip2zip}", "${soong_zip}", "${merge_zips}"}, 156 Description: "app bundle", 157 }, "abi", "config") 158 159 emitApexContentRule = pctx.StaticRule("emitApexContentRule", blueprint.RuleParams{ 160 Command: `rm -f ${out} && touch ${out} && (. ${out}.emit_commands)`, 161 Rspfile: "${out}.emit_commands", 162 RspfileContent: "${emit_commands}", 163 Description: "Emit APEX image content", 164 }, "emit_commands") 165 166 diffApexContentRule = pctx.StaticRule("diffApexContentRule", blueprint.RuleParams{ 167 Command: `diff --unchanged-group-format='' \` + 168 `--changed-group-format='%<' \` + 169 `${image_content_file} ${allowed_files_file} || (` + 170 `echo -e "New unexpected files were added to ${apex_module_name}." ` + 171 ` "To fix the build run following command:" && ` + 172 `echo "system/apex/tools/update_allowed_list.sh ${allowed_files_file} ${image_content_file}" && ` + 173 `exit 1); touch ${out}`, 174 Description: "Diff ${image_content_file} and ${allowed_files_file}", 175 }, "image_content_file", "allowed_files_file", "apex_module_name") 176) 177 178func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, requireNativeLibs []string) { 179 manifestSrc := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json")) 180 181 manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json") 182 183 // put dependency({provide|require}NativeLibs) in apex_manifest.json 184 provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs) 185 requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs)) 186 187 // apex name can be overridden 188 optCommands := []string{} 189 if a.properties.Apex_name != nil { 190 optCommands = append(optCommands, "-v name "+*a.properties.Apex_name) 191 } 192 193 // collect jniLibs. Notice that a.filesInfo is already sorted 194 var jniLibs []string 195 for _, fi := range a.filesInfo { 196 if fi.isJniLib { 197 jniLibs = append(jniLibs, fi.Stem()) 198 } 199 } 200 if len(jniLibs) > 0 { 201 optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " ")) 202 } 203 204 ctx.Build(pctx, android.BuildParams{ 205 Rule: apexManifestRule, 206 Input: manifestSrc, 207 Output: manifestJsonFullOut, 208 Args: map[string]string{ 209 "provideNativeLibs": strings.Join(provideNativeLibs, " "), 210 "requireNativeLibs": strings.Join(requireNativeLibs, " "), 211 "opt": strings.Join(optCommands, " "), 212 }, 213 }) 214 215 if a.minSdkVersion(ctx) == android.SdkVersion_Android10 { 216 // b/143654022 Q apexd can't understand newly added keys in apex_manifest.json 217 // prepare stripped-down version so that APEX modules built from R+ can be installed to Q 218 a.manifestJsonOut = android.PathForModuleOut(ctx, "apex_manifest.json") 219 ctx.Build(pctx, android.BuildParams{ 220 Rule: stripApexManifestRule, 221 Input: manifestJsonFullOut, 222 Output: a.manifestJsonOut, 223 }) 224 } 225 226 // from R+, protobuf binary format (.pb) is the standard format for apex_manifest 227 a.manifestPbOut = android.PathForModuleOut(ctx, "apex_manifest.pb") 228 ctx.Build(pctx, android.BuildParams{ 229 Rule: pbApexManifestRule, 230 Input: manifestJsonFullOut, 231 Output: a.manifestPbOut, 232 }) 233} 234 235func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) { 236 if a.properties.ApexType == zipApex { 237 return 238 } 239 var fileContexts android.Path 240 if a.properties.File_contexts == nil { 241 fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts") 242 } else { 243 fileContexts = android.PathForModuleSrc(ctx, *a.properties.File_contexts) 244 } 245 if a.Platform() { 246 if matched, err := path.Match("system/sepolicy/**/*", fileContexts.String()); err != nil || !matched { 247 ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but %q", fileContexts) 248 return 249 } 250 } 251 if !android.ExistentPathForSource(ctx, fileContexts.String()).Valid() { 252 ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", a.fileContexts) 253 return 254 } 255 256 output := android.PathForModuleOut(ctx, "file_contexts") 257 rule := android.NewRuleBuilder() 258 // remove old file 259 rule.Command().Text("rm").FlagWithOutput("-f ", output) 260 // copy file_contexts 261 rule.Command().Text("cat").Input(fileContexts).Text(">>").Output(output) 262 // new line 263 rule.Command().Text("echo").Text(">>").Output(output) 264 // force-label /apex_manifest.pb and / as system_file so that apexd can read them 265 rule.Command().Text("echo").Flag("/apex_manifest\\\\.pb u:object_r:system_file:s0").Text(">>").Output(output) 266 rule.Command().Text("echo").Flag("/ u:object_r:system_file:s0").Text(">>").Output(output) 267 rule.Build(pctx, ctx, "file_contexts."+a.Name(), "Generate file_contexts") 268 269 a.fileContexts = output.OutputPath 270} 271 272func (a *apexBundle) buildNoticeFiles(ctx android.ModuleContext, apexFileName string) android.NoticeOutputs { 273 var noticeFiles android.Paths 274 275 a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { 276 if externalDep { 277 // As soon as the dependency graph crosses the APEX boundary, don't go further. 278 return false 279 } 280 281 notices := to.NoticeFiles() 282 noticeFiles = append(noticeFiles, notices...) 283 284 return true 285 }) 286 287 if len(noticeFiles) == 0 { 288 return android.NoticeOutputs{} 289 } 290 291 return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.SortedUniquePaths(noticeFiles)) 292} 293 294func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.OutputPath { 295 output := android.PathForModuleOut(ctx, "installed-files.txt") 296 rule := android.NewRuleBuilder() 297 rule.Command(). 298 Implicit(builtApex). 299 Text("(cd " + imageDir.String() + " ; "). 300 Text("find . \\( -type f -o -type l \\) -printf \"%s %p\\n\") "). 301 Text(" | sort -nr > "). 302 Output(output) 303 rule.Build(pctx, ctx, "installed-files."+a.Name(), "Installed files") 304 return output.OutputPath 305} 306 307func (a *apexBundle) buildBundleConfig(ctx android.ModuleContext) android.OutputPath { 308 output := android.PathForModuleOut(ctx, "bundle_config.json") 309 310 type ApkConfig struct { 311 Package_name string `json:"package_name"` 312 Apk_path string `json:"path"` 313 } 314 config := struct { 315 Compression struct { 316 Uncompressed_glob []string `json:"uncompressed_glob"` 317 } `json:"compression"` 318 Apex_config struct { 319 Apex_embedded_apk_config []ApkConfig `json:"apex_embedded_apk_config,omitempty"` 320 } `json:"apex_config,omitempty"` 321 }{} 322 323 config.Compression.Uncompressed_glob = []string{ 324 "apex_payload.img", 325 "apex_manifest.*", 326 } 327 328 // collect the manifest names and paths of android apps 329 // if their manifest names are overridden 330 for _, fi := range a.filesInfo { 331 if fi.class != app && fi.class != appSet { 332 continue 333 } 334 packageName := fi.overriddenPackageName 335 if packageName != "" { 336 config.Apex_config.Apex_embedded_apk_config = append( 337 config.Apex_config.Apex_embedded_apk_config, 338 ApkConfig{ 339 Package_name: packageName, 340 Apk_path: fi.Path(), 341 }) 342 } 343 } 344 345 j, err := json.Marshal(config) 346 if err != nil { 347 panic(fmt.Errorf("error while marshalling to %q: %#v", output, err)) 348 } 349 350 ctx.Build(pctx, android.BuildParams{ 351 Rule: android.WriteFile, 352 Output: output, 353 Description: "Bundle Config " + output.String(), 354 Args: map[string]string{ 355 "content": string(j), 356 }, 357 }) 358 359 return output.OutputPath 360} 361 362func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { 363 var abis []string 364 for _, target := range ctx.MultiTargets() { 365 if len(target.Arch.Abi) > 0 { 366 abis = append(abis, target.Arch.Abi[0]) 367 } 368 } 369 370 abis = android.FirstUniqueStrings(abis) 371 372 apexType := a.properties.ApexType 373 suffix := apexType.suffix() 374 var implicitInputs []android.Path 375 unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned") 376 377 // TODO(jiyong): construct the copy rules using RuleBuilder 378 var copyCommands []string 379 for _, fi := range a.filesInfo { 380 destPath := android.PathForModuleOut(ctx, "image"+suffix, fi.Path()).String() 381 destPathDir := filepath.Dir(destPath) 382 if fi.class == appSet { 383 copyCommands = append(copyCommands, "rm -rf "+destPathDir) 384 } 385 copyCommands = append(copyCommands, "mkdir -p "+destPathDir) 386 if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() { 387 // TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here 388 pathOnDevice := filepath.Join("/system", fi.Path()) 389 copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath) 390 } else { 391 if fi.class == appSet { 392 copyCommands = append(copyCommands, 393 fmt.Sprintf("unzip -qDD -d %s %s", destPathDir, fi.builtFile.String())) 394 } else { 395 copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath) 396 } 397 implicitInputs = append(implicitInputs, fi.builtFile) 398 } 399 // create additional symlinks pointing the file inside the APEX 400 for _, symlinkPath := range fi.SymlinkPaths() { 401 symlinkDest := android.PathForModuleOut(ctx, "image"+suffix, symlinkPath).String() 402 copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest) 403 } 404 for _, d := range fi.dataPaths { 405 // TODO(eakammer): This is now the third repetition of ~this logic for test paths, refactoring should be possible 406 relPath := d.SrcPath.Rel() 407 dataPath := d.SrcPath.String() 408 if !strings.HasSuffix(dataPath, relPath) { 409 panic(fmt.Errorf("path %q does not end with %q", dataPath, relPath)) 410 } 411 412 dataDest := android.PathForModuleOut(ctx, "image"+suffix, fi.apexRelativePath(relPath), d.RelativeInstallPath).String() 413 414 copyCommands = append(copyCommands, "cp -f "+d.SrcPath.String()+" "+dataDest) 415 implicitInputs = append(implicitInputs, d.SrcPath) 416 } 417 } 418 419 // TODO(jiyong): use RuleBuilder 420 var emitCommands []string 421 imageContentFile := android.PathForModuleOut(ctx, "content.txt") 422 emitCommands = append(emitCommands, "echo ./apex_manifest.pb >> "+imageContentFile.String()) 423 if a.minSdkVersion(ctx) == android.SdkVersion_Android10 { 424 emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String()) 425 } 426 for _, fi := range a.filesInfo { 427 emitCommands = append(emitCommands, "echo './"+fi.Path()+"' >> "+imageContentFile.String()) 428 } 429 emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String()) 430 implicitInputs = append(implicitInputs, a.manifestPbOut) 431 432 if a.overridableProperties.Allowed_files != nil { 433 ctx.Build(pctx, android.BuildParams{ 434 Rule: emitApexContentRule, 435 Implicits: implicitInputs, 436 Output: imageContentFile, 437 Description: "emit apex image content", 438 Args: map[string]string{ 439 "emit_commands": strings.Join(emitCommands, " && "), 440 }, 441 }) 442 implicitInputs = append(implicitInputs, imageContentFile) 443 allowedFilesFile := android.PathForModuleSrc(ctx, proptools.String(a.overridableProperties.Allowed_files)) 444 445 phonyOutput := android.PathForModuleOut(ctx, a.Name()+"-diff-phony-output") 446 ctx.Build(pctx, android.BuildParams{ 447 Rule: diffApexContentRule, 448 Implicits: implicitInputs, 449 Output: phonyOutput, 450 Description: "diff apex image content", 451 Args: map[string]string{ 452 "allowed_files_file": allowedFilesFile.String(), 453 "image_content_file": imageContentFile.String(), 454 "apex_module_name": a.Name(), 455 }, 456 }) 457 458 implicitInputs = append(implicitInputs, phonyOutput) 459 } 460 461 outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String() 462 prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin") 463 464 imageDir := android.PathForModuleOut(ctx, "image"+suffix) 465 if apexType == imageApex { 466 // files and dirs that will be created in APEX 467 var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"} 468 var executablePaths []string // this also includes dirs 469 var extractedAppSetPaths android.Paths 470 var extractedAppSetDirs []string 471 for _, f := range a.filesInfo { 472 pathInApex := f.Path() 473 if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") { 474 executablePaths = append(executablePaths, pathInApex) 475 for _, d := range f.dataPaths { 476 readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.RelativeInstallPath, d.SrcPath.Rel())) 477 } 478 for _, s := range f.symlinks { 479 executablePaths = append(executablePaths, filepath.Join(f.installDir, s)) 480 } 481 } else if f.class == appSet { 482 extractedAppSetPaths = append(extractedAppSetPaths, f.builtFile) 483 extractedAppSetDirs = append(extractedAppSetDirs, f.installDir) 484 } else { 485 readOnlyPaths = append(readOnlyPaths, pathInApex) 486 } 487 dir := f.installDir 488 for !android.InList(dir, executablePaths) && dir != "" { 489 executablePaths = append(executablePaths, dir) 490 dir, _ = filepath.Split(dir) // move up to the parent 491 if len(dir) > 0 { 492 // remove trailing slash 493 dir = dir[:len(dir)-1] 494 } 495 } 496 } 497 sort.Strings(readOnlyPaths) 498 sort.Strings(executablePaths) 499 cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config") 500 ctx.Build(pctx, android.BuildParams{ 501 Rule: generateFsConfig, 502 Output: cannedFsConfig, 503 Description: "generate fs config", 504 Inputs: extractedAppSetPaths, 505 Args: map[string]string{ 506 "ro_paths": strings.Join(readOnlyPaths, " "), 507 "exec_paths": strings.Join(executablePaths, " "), 508 "apk_paths": strings.Join(extractedAppSetDirs, " "), 509 }, 510 }) 511 512 optFlags := []string{} 513 514 // Additional implicit inputs. 515 implicitInputs = append(implicitInputs, cannedFsConfig, a.fileContexts, a.private_key_file, a.public_key_file) 516 optFlags = append(optFlags, "--pubkey "+a.public_key_file.String()) 517 518 manifestPackageName := a.getOverrideManifestPackageName(ctx) 519 if manifestPackageName != "" { 520 optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName) 521 } 522 523 if a.properties.AndroidManifest != nil { 524 androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest)) 525 implicitInputs = append(implicitInputs, androidManifestFile) 526 optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String()) 527 } 528 529 targetSdkVersion := ctx.Config().DefaultAppTargetSdk() 530 // TODO(b/157078772): propagate min_sdk_version to apexer. 531 minSdkVersion := ctx.Config().DefaultAppTargetSdk() 532 533 if a.minSdkVersion(ctx) == android.SdkVersion_Android10 { 534 minSdkVersion = strconv.Itoa(a.minSdkVersion(ctx)) 535 } 536 537 if java.UseApiFingerprint(ctx) { 538 targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String()) 539 implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx)) 540 } 541 if java.UseApiFingerprint(ctx) { 542 minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String()) 543 implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx)) 544 } 545 optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion) 546 optFlags = append(optFlags, "--min_sdk_version "+minSdkVersion) 547 548 if a.overridableProperties.Logging_parent != "" { 549 optFlags = append(optFlags, "--logging_parent ", a.overridableProperties.Logging_parent) 550 } 551 552 a.mergedNotices = a.buildNoticeFiles(ctx, a.Name()+suffix) 553 if a.mergedNotices.HtmlGzOutput.Valid() { 554 // If there's a NOTICE file, embed it as an asset file in the APEX. 555 implicitInputs = append(implicitInputs, a.mergedNotices.HtmlGzOutput.Path()) 556 optFlags = append(optFlags, "--assets_dir "+filepath.Dir(a.mergedNotices.HtmlGzOutput.String())) 557 } 558 559 if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && ctx.ModuleDir() != "system/apex/shim/build" && a.testOnlyShouldSkipHashtreeGeneration() { 560 ctx.PropertyErrorf("test_only_no_hashtree", "not available") 561 return 562 } 563 if a.minSdkVersion(ctx) > android.SdkVersion_Android10 || a.testOnlyShouldSkipHashtreeGeneration() { 564 // Apexes which are supposed to be installed in builtin dirs(/system, etc) 565 // don't need hashtree for activation. Therefore, by removing hashtree from 566 // apex bundle (filesystem image in it, to be specific), we can save storage. 567 optFlags = append(optFlags, "--no_hashtree") 568 } 569 570 if a.testOnlyShouldSkipPayloadSign() { 571 optFlags = append(optFlags, "--unsigned_payload") 572 } 573 574 if a.properties.Apex_name != nil { 575 // If apex_name is set, apexer can skip checking if key name matches with apex name. 576 // Note that apex_manifest is also mended. 577 optFlags = append(optFlags, "--do_not_check_keyname") 578 } 579 580 if a.minSdkVersion(ctx) == android.SdkVersion_Android10 { 581 implicitInputs = append(implicitInputs, a.manifestJsonOut) 582 optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String()) 583 } 584 585 ctx.Build(pctx, android.BuildParams{ 586 Rule: apexRule, 587 Implicits: implicitInputs, 588 Output: unsignedOutputFile, 589 Description: "apex (" + apexType.name() + ")", 590 Args: map[string]string{ 591 "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, 592 "image_dir": imageDir.String(), 593 "copy_commands": strings.Join(copyCommands, " && "), 594 "manifest": a.manifestPbOut.String(), 595 "file_contexts": a.fileContexts.String(), 596 "canned_fs_config": cannedFsConfig.String(), 597 "key": a.private_key_file.String(), 598 "opt_flags": strings.Join(optFlags, " "), 599 }, 600 }) 601 602 apexProtoFile := android.PathForModuleOut(ctx, a.Name()+".pb"+suffix) 603 bundleModuleFile := android.PathForModuleOut(ctx, a.Name()+suffix+"-base.zip") 604 a.bundleModuleFile = bundleModuleFile 605 606 ctx.Build(pctx, android.BuildParams{ 607 Rule: apexProtoConvertRule, 608 Input: unsignedOutputFile, 609 Output: apexProtoFile, 610 Description: "apex proto convert", 611 }) 612 613 bundleConfig := a.buildBundleConfig(ctx) 614 615 ctx.Build(pctx, android.BuildParams{ 616 Rule: apexBundleRule, 617 Input: apexProtoFile, 618 Implicit: bundleConfig, 619 Output: a.bundleModuleFile, 620 Description: "apex bundle module", 621 Args: map[string]string{ 622 "abi": strings.Join(abis, "."), 623 "config": bundleConfig.String(), 624 }, 625 }) 626 } else { 627 ctx.Build(pctx, android.BuildParams{ 628 Rule: zipApexRule, 629 Implicits: implicitInputs, 630 Output: unsignedOutputFile, 631 Description: "apex (" + apexType.name() + ")", 632 Args: map[string]string{ 633 "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, 634 "image_dir": imageDir.String(), 635 "copy_commands": strings.Join(copyCommands, " && "), 636 "manifest": a.manifestPbOut.String(), 637 }, 638 }) 639 } 640 641 a.outputFile = android.PathForModuleOut(ctx, a.Name()+suffix) 642 rule := java.Signapk 643 args := map[string]string{ 644 "certificates": a.container_certificate_file.String() + " " + a.container_private_key_file.String(), 645 "flags": "-a 4096", //alignment 646 } 647 implicits := android.Paths{ 648 a.container_certificate_file, 649 a.container_private_key_file, 650 } 651 if ctx.Config().IsEnvTrue("RBE_SIGNAPK") { 652 rule = java.SignapkRE 653 args["implicits"] = strings.Join(implicits.Strings(), ",") 654 args["outCommaList"] = a.outputFile.String() 655 } 656 ctx.Build(pctx, android.BuildParams{ 657 Rule: rule, 658 Description: "signapk", 659 Output: a.outputFile, 660 Input: unsignedOutputFile, 661 Implicits: implicits, 662 Args: args, 663 }) 664 665 // Install to $OUT/soong/{target,host}/.../apex 666 if a.installable() { 667 ctx.InstallFile(a.installDir, a.Name()+suffix, a.outputFile) 668 } 669 a.buildFilesInfo(ctx) 670 671 // installed-files.txt is dist'ed 672 a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir) 673} 674 675func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) { 676 // Temporarily wrap the original `ctx` into a `flattenedApexContext` to have it 677 // reply true to `InstallBypassMake()` (thus making the call 678 // `android.PathForModuleInstall` below use `android.pathForInstallInMakeDir` 679 // instead of `android.PathForOutput`) to return the correct path to the flattened 680 // APEX (as its contents is installed by Make, not Soong). 681 factx := flattenedApexContext{ctx} 682 apexBundleName := a.Name() 683 a.outputFile = android.PathForModuleInstall(&factx, "apex", apexBundleName) 684 685 if a.installable() && a.GetOverriddenBy() == "" { 686 installPath := android.PathForModuleInstall(ctx, "apex", apexBundleName) 687 devicePath := android.InstallPathToOnDevicePath(ctx, installPath) 688 addFlattenedFileContextsInfos(ctx, apexBundleName+":"+devicePath+":"+a.fileContexts.String()) 689 } 690 a.buildFilesInfo(ctx) 691} 692 693func (a *apexBundle) setCertificateAndPrivateKey(ctx android.ModuleContext) { 694 if a.container_certificate_file == nil { 695 cert := String(a.properties.Certificate) 696 if cert == "" { 697 pem, key := ctx.Config().DefaultAppCertificate(ctx) 698 a.container_certificate_file = pem 699 a.container_private_key_file = key 700 } else { 701 defaultDir := ctx.Config().DefaultAppCertificateDir(ctx) 702 a.container_certificate_file = defaultDir.Join(ctx, cert+".x509.pem") 703 a.container_private_key_file = defaultDir.Join(ctx, cert+".pk8") 704 } 705 } 706} 707 708func (a *apexBundle) buildFilesInfo(ctx android.ModuleContext) { 709 if a.installable() { 710 // For flattened APEX, do nothing but make sure that APEX manifest and apex_pubkey are also copied along 711 // with other ordinary files. 712 a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb", ".", etc, nil)) 713 714 // rename to apex_pubkey 715 copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey") 716 ctx.Build(pctx, android.BuildParams{ 717 Rule: android.Cp, 718 Input: a.public_key_file, 719 Output: copiedPubkey, 720 }) 721 a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey", ".", etc, nil)) 722 723 if a.properties.ApexType == flattenedApex { 724 apexBundleName := a.Name() 725 for _, fi := range a.filesInfo { 726 dir := filepath.Join("apex", apexBundleName, fi.installDir) 727 target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.Stem(), fi.builtFile) 728 for _, sym := range fi.symlinks { 729 ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target) 730 } 731 } 732 } 733 } 734} 735 736func (a *apexBundle) getOverrideManifestPackageName(ctx android.ModuleContext) string { 737 // For VNDK APEXes, check "com.android.vndk" in PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES 738 // to see if it should be overridden because their <apex name> is dynamically generated 739 // according to its VNDK version. 740 if a.vndkApex { 741 overrideName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(vndkApexName) 742 if overridden { 743 return strings.Replace(*a.properties.Apex_name, vndkApexName, overrideName, 1) 744 } 745 return "" 746 } 747 if a.overridableProperties.Package_name != "" { 748 return a.overridableProperties.Package_name 749 } 750 manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName()) 751 if overridden { 752 return manifestPackageName 753 } 754 return "" 755} 756 757func (a *apexBundle) buildApexDependencyInfo(ctx android.ModuleContext) { 758 if !a.primaryApexType { 759 return 760 } 761 762 if a.properties.IsCoverageVariant { 763 // Otherwise, we will have duplicated rules for coverage and 764 // non-coverage variants of the same APEX 765 return 766 } 767 768 if ctx.Host() { 769 // No need to generate dependency info for host variant 770 return 771 } 772 773 depInfos := android.DepNameToDepInfoMap{} 774 a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { 775 if from.Name() == to.Name() { 776 // This can happen for cc.reuseObjTag. We are not interested in tracking this. 777 // As soon as the dependency graph crosses the APEX boundary, don't go further. 778 return !externalDep 779 } 780 781 if info, exists := depInfos[to.Name()]; exists { 782 if !android.InList(from.Name(), info.From) { 783 info.From = append(info.From, from.Name()) 784 } 785 info.IsExternal = info.IsExternal && externalDep 786 depInfos[to.Name()] = info 787 } else { 788 toMinSdkVersion := "(no version)" 789 if m, ok := to.(interface{ MinSdkVersion() string }); ok { 790 if v := m.MinSdkVersion(); v != "" { 791 toMinSdkVersion = v 792 } 793 } 794 795 depInfos[to.Name()] = android.ApexModuleDepInfo{ 796 To: to.Name(), 797 From: []string{from.Name()}, 798 IsExternal: externalDep, 799 MinSdkVersion: toMinSdkVersion, 800 } 801 } 802 803 // As soon as the dependency graph crosses the APEX boundary, don't go further. 804 return !externalDep 805 }) 806 807 a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, proptools.String(a.properties.Min_sdk_version), depInfos) 808 809 ctx.Build(pctx, android.BuildParams{ 810 Rule: android.Phony, 811 Output: android.PathForPhony(ctx, a.Name()+"-deps-info"), 812 Inputs: []android.Path{ 813 a.ApexBundleDepsInfo.FullListPath(), 814 a.ApexBundleDepsInfo.FlatListPath(), 815 }, 816 }) 817} 818 819func (a *apexBundle) buildLintReports(ctx android.ModuleContext) { 820 depSetsBuilder := java.NewLintDepSetBuilder() 821 for _, fi := range a.filesInfo { 822 depSetsBuilder.Transitive(fi.lintDepSets) 823 } 824 825 a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build()) 826} 827