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