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