Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/soong/apex/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 55 kB image not shown  

Quelle  builder.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apex

import (
 "encoding/json"
 "fmt"
 "path"
 "path/filepath"
 "runtime"
 "slices"
 "sort"
 "strconv"
 "strings"

 "android/soong/aconfig"
 "android/soong/android"
 "android/soong/dexpreopt"
 "android/soong/java"

 "github.com/google/blueprint"
 "github.com/google/blueprint/proptools"
)

var (
 pctx = android.NewPackageContext("android/apex")
)

func init() {
 pctx.Import("android/soong/aconfig")
 pctx.Import("android/soong/android")
 pctx.Import("android/soong/cc/config")
 pctx.Import("android/soong/java")
 pctx.HostBinToolVariable("apexer", "apexer")
 pctx.HostBinToolVariable("apexer_with_DCLA_preprocessing", "apexer_with_DCLA_preprocessing")

 // ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
 // projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead.
 hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
  pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
   if !ctx.Config().FrameworksBaseDirExists(ctx) {
    return filepath.Join(prebuiltDir, runtime.GOOS, "bin", tool)
   } else {
    return ctx.Config().HostToolPath(ctx, tool).String()
   }
  })
 }
 hostBinToolVariableWithPrebuilt("aapt2", "prebuilts/sdk/tools", "aapt2")
 pctx.SourcePathVariable("openssl", "prebuilts/build-tools/${android.HostPrebuiltTag}/bin/openssl")
}

type createStorageStruct struct {
 Output_file string
 Desc        string
 File_type   string
}

var createStorageInfo = []createStorageStruct{
 {"package.map", "create_aconfig_package_map_file", "package_map"},
 {"flag.map", "create_aconfig_flag_map_file", "flag_map"},
 {"flag.val", "create_aconfig_flag_val_file", "flag_val"},
 {"flag.info", "create_aconfig_flag_info_file", "flag_info"},
}

var (
 apexer                         = pctx.HostTool("apexer")
 apexer_with_DCLA_preprocessing = pctx.HostTool("apexer_with_DCLA_preprocessing")
 avbtool                        = pctx.HostTool("avbtool")
 e2fsdroid                      = pctx.HostTool("e2fsdroid")
 mke2fs                         = pctx.HostTool("mke2fs")
 resize2fs                      = pctx.HostTool("resize2fs")
 sefcontext_compile             = pctx.HostTool("sefcontext_compile")
 make_f2fs                      = pctx.HostTool("make_f2fs")
 sload_f2fs                     = pctx.HostTool("sload_f2fs")
 make_erofs                     = pctx.HostTool("mkfs.erofs")
 zipalign                       = pctx.HostTool("zipalign")
 apex_elf_checker               = pctx.HostTool("apex_elf_checker")
 host_apex_verifier             = pctx.HostTool("host_apex_verifier")
 deapexer                       = pctx.HostTool("deapexer")
 debugfs                        = pctx.HostTool("debugfs")
 fsck_erofs                     = pctx.HostTool("fsck.erofs")
 aconfigTool                    = aconfig.Aconfig
 apex_ls                        = pctx.HostTool("apex-ls")
 apex_sepolicy_tests            = pctx.HostTool("apex_sepolicy_tests")
 zip2zip                        = pctx.HostTool("zip2zip")
 jsonmodify                     = pctx.HostTool("jsonmodify")
 conv_apex_manifest             = pctx.HostTool("conv_apex_manifest")
 conv_linker_config             = pctx.HostTool("conv_linker_config")
 extract_apks                   = pctx.HostTool("extract_apks")
 gen_apex_symbols               = pctx.HostTool("gen_apex_symbols")

 apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   android.Rm, ` -f $out && `, jsonmodify, ` $in `,
   `-a provideNativeLibs ${provideNativeLibs} `,
   `-a requireNativeLibs ${requireNativeLibs} `,
   `-se version 0 ${default_version} `,
   `${opt} -o $out`,
  ),
  Description: "prepare ${out}",
 }, "provideNativeLibs", "requireNativeLibs", "default_version", "opt")

 stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   android.Rm, ` -f $out && `, conv_apex_manifest, ` strip $in -o $out`,
  ),
  Description: "strip ${in}=>${out}",
 })

 pbApexManifestRule = pctx.StaticRule("pbApexManifestRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   android.Rm, ` -f $out && `, conv_apex_manifest, ` proto $in -o $out`,
  ),
  Description: "convert ${in}=>${out}",
 })

 // TODO(b/113233103): make sure that file_contexts is as expected, i.e., validate
 // against the binary policy using sefcontext_compiler -p <policy>.

 // TODO(b/114327326): automate the generation of file_contexts
 apexRule = pctx.StaticRule("apexRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   android.Rm, ` -rf ${image_dir} && `, android.Mkdir, ` -p ${image_dir} && `,
   `(. ${out}.copy_commands) && `,
   `APEXER_TOOL_PATH=${tool_path} `,
   apexer, ` --force --manifest ${manifest} `,
   `--file_contexts ${file_contexts} `,
   `--canned_fs_config ${canned_fs_config} `,
   `--include_build_info `,
   `--payload_type image `,
   `--key ${key} ${opt_flags} ${image_dir} ${out} && `,
   android.SoongZip, ` -d -C ${image_dir} -D ${image_dir} -o ${image_zip}`,
  ),
  CommandDeps: []string{"${aapt2}", "prebuilts/sdk/current/public/android.jar"},
  CommandDepsTools: []blueprint.HostTool{avbtool, e2fsdroid, android.MergeZips,
   mke2fs, resize2fs, sefcontext_compile, make_f2fs, sload_f2fs, make_erofs,
   android.SoongZip, zipalign, android.Cp, android.Ln, android.ZipSync},
  Rspfile:        "${out}.copy_commands",
  RspfileContent: "${copy_commands}",
  Description:    "APEX ${image_dir} => ${out}",
 }, "tool_path", "image_dir", "image_zip", "copy_commands", "file_contexts", "canned_fs_config", "key",
  "opt_flags", "manifest")

 DCLAApexRule = pctx.StaticRule("DCLAApexRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   android.Rm, ` -rf ${image_dir} && `, android.Mkdir, ` -p ${image_dir} && `,
   `(. ${out}.copy_commands) && `,
   `APEXER_TOOL_PATH=${tool_path} `,
   apexer_with_DCLA_preprocessing, ` --apexer `, apexer,
   ` --canned_fs_config ${canned_fs_config} `,
   `${image_dir} `,
   `${out} `,
   `-- `,
   `--include_build_info `,
   `--force `,
   `--payload_type image `,
   `--key ${key} `,
   `--file_contexts ${file_contexts} `,
   `--manifest ${manifest} `,
   `${opt_flags} && `,
   android.SoongZip, ` -d -C ${image_dir} -D ${image_dir} -o ${image_zip}`,
  ),
  CommandDeps: []string{"${aapt2}", "prebuilts/sdk/current/public/android.jar"},
  CommandDepsTools: []blueprint.HostTool{avbtool, e2fsdroid, android.MergeZips,
   mke2fs, resize2fs, sefcontext_compile, make_f2fs, sload_f2fs, make_erofs,
   android.SoongZip, zipalign, android.Cp, android.Ln, android.ZipSync},
  Rspfile:        "${out}.copy_commands",
  RspfileContent: "${copy_commands}",
  Description:    "APEX ${image_dir} => ${out}",
 }, "tool_path", "image_dir", "image_zip", "copy_commands", "file_contexts", "canned_fs_config", "key",
  "opt_flags", "manifest", "is_DCLA")

 apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
  blueprint.RuleParams{
   Command:     `${aapt2} convert --output-format proto $in -o $out`,
   CommandDeps: []string{"${aapt2}"},
  })

 apexBundleRule = pctx.StaticRule("apexBundleRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   zip2zip, ` -i $in -o $out.base `,
   `apex_payload.img:apex/${abi}.img `,
   `apex_build_info.pb:apex/${abi}.build_info.pb `,
   `apex_manifest.json:root/apex_manifest.json `,
   `apex_manifest.pb:root/apex_manifest.pb `,
   `AndroidManifest.xml:manifest/AndroidManifest.xml `,
   `assets/NOTICE.html.gz:assets/NOTICE.html.gz &&`,
   android.SoongZip, ` -o $out.config -C $$(dirname ${config}) -f ${config} && `,
   android.MergeZips, ` $out $out.base $out.config`,
  ),
  Description: "app bundle",
 }, "abi", "config")

 diffApexContentRule = pctx.StaticRule("diffApexContentRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   `diff --unchanged-group-format='' \`,
   `--changed-group-format='%<' \`,
   `${image_content_file} ${allowed_files_file} || (`,
   android.Echo, ` "New unexpected files were added to ${apex_module_name}." `,
   ` "To fix the build run following command:" && `,
   android.Echo, ` "system/apex/tools/update_allowed_list.sh ${allowed_files_file} ${image_content_file}" && `,
   `exit 1); `, android.Touch, ` ${out}`,
  ),
  Description: "Diff ${image_content_file} and ${allowed_files_file}",
 }, "image_content_file", "allowed_files_file", "apex_module_name")

 generateAPIsUsedbyApexRule = pctx.StaticRule("generateAPIsUsedbyApexRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   android.Rm, ` -rf ${out}.image && `, android.Mkdir, ` -p ${out}.image && `,
   android.ZipSync, ` -d ${out}.image ${image_zip} && `,
   gen_apex_symbols, ` ndk_usedby ${out}.image ${config.ClangBin}/llvm-readelf `, android.ZipSync, ` ${out} && `,
   android.Rm, ` -rf ${out}.image`,
  ),
  CommandDeps: []string{
   "${config.ClangBin}/llvm-readelf",
   "${config.ClangBin}/llvm-readobj",
  },
  Description: "Generate symbol list used by Apex",
 }, "image_zip")

 apexSepolicyTestsRule = pctx.StaticRule("apexSepolicyTestsRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   apex_ls, ` -Z ${in} > ${out}.fc`,
   ` &&`, apex_sepolicy_tests, ` -f ${out}.fc --partition ${partition_tag}`,
   ` &&`, android.Touch, ` ${out}`,
  ),
  Description: "run apex_sepolicy_tests",
 }, "partition_tag")

 apexLinkerconfigValidationRule = pctx.StaticRule("apexLinkerconfigValidationRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   android.Rm, ` -rf ${out}.image && `, android.Mkdir, ` -p ${out}.image && `,
   android.ZipSync, ` -d ${out}.image ${image_zip} && `,
   conv_linker_config, ` validate --type apex ${out}.image && `,
   android.Touch, ` ${out} && `,
   android.Rm, ` -rf ${out}.image`,
  ),
  Description: "run apex_linkerconfig_validation",
 }, "image_zip")

 apexHostVerifierRule = pctx.StaticRule("apexHostVerifierRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   host_apex_verifier, ` --deapexer=`, deapexer, ` --debugfs=`, debugfs, ` `,
   `--fsckerofs=`, fsck_erofs, ` --apex=${in} --partition_tag=${partition_tag} && `,
   android.Touch, ` ${out}`),
  Description: "run host_apex_verifier",
 }, "partition_tag")

 apexElfCheckerUnwantedRule = pctx.StaticRule("apexElfCheckerUnwantedRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   apex_elf_checker, ` --tool_path ${tool_path} --unwanted ${unwanted} ${in} && `,
   android.Touch, ` ${out}`),
  CommandDeps: []string{
   "${config.ClangBin}/llvm-readelf",
   "${config.ClangBin}/llvm-readobj",
  },
  CommandDepsTools: []blueprint.HostTool{deapexer, debugfs, fsck_erofs},
  Description:      "run apex_elf_checker --unwanted",
 }, "tool_path", "unwanted")

 apexAconfigFlagsPbRule = pctx.StaticRule("apexAconfigFlagsPbRule", blueprint.RuleParams{
  Command2: blueprint.NewCommand(
   aconfigTool, ` dump-cache --dedup --format protobuf --out ${out} `,
   `--filter container:${container}+namespace:!${beta_namespace} ${cache_files}`),
  Description: "create aconfig_flags.pb file",
 }, "container", "beta_namespace", "cache_files")
)

func (a *apexBundle) buildAconfigFiles(ctx android.ModuleContext) []apexFile {
 var aconfigFiles android.Paths
 for _, file := range a.filesInfo {
  if file.module.IsNil() {
   continue
  }
  if dep, ok := android.OtherModuleProvider(ctx, file.module, android.CommonModuleInfoProvider); ok && dep.AconfigPropagatingDeclarations != nil {
   if len(dep.AconfigPropagatingDeclarations.AconfigFiles) > 0 && dep.AconfigPropagatingDeclarations.AconfigFiles[ctx.ModuleName()] != nil {
    aconfigFiles = append(aconfigFiles, dep.AconfigPropagatingDeclarations.AconfigFiles[ctx.ModuleName()]...)
   }
  }

  validationFlag := ctx.DeviceConfig().AconfigContainerValidation()
  if validationFlag == "error" || validationFlag == "warning" {
   android.VerifyAconfigBuildMode(ctx, ctx.ModuleName(), file.module, validationFlag == "error")
  }
 }
 aconfigFiles = android.FirstUniquePaths(aconfigFiles)

 var files []apexFile
 if len(aconfigFiles) > 0 {
  apexAconfigFile := android.PathForModuleOut(ctx, "aconfig_flags.pb")
  if ctx.Config().ReleaseRemoveBetaFlagsFromAconfigFlagsPb() {
   beta_namespace := ""
   if a.overridableProperties.Beta_namespace != nil {
    beta_namespace = *(a.overridableProperties.Beta_namespace)
   }
   ctx.Build(pctx, android.BuildParams{
    Rule:        apexAconfigFlagsPbRule,
    Inputs:      aconfigFiles,
    Output:      apexAconfigFile,
    Description: "aggregate all dependent aconfig caches",
    Args: map[string]string{
     "container":      ctx.ModuleName(),
     "beta_namespace": beta_namespace,
     "cache_files":    android.JoinPathsWithPrefix(aconfigFiles, "--cache "),
    },
   })
  } else {
   ctx.Build(pctx, android.BuildParams{
    Rule:        aconfig.AllDeclarationsRule,
    Inputs:      aconfigFiles,
    Output:      apexAconfigFile,
    Description: "combine_aconfig_declarations",
    Args: map[string]string{
     "cache_files": android.JoinPathsWithPrefix(aconfigFiles, "--cache "),
    },
   })
  }
  files = append(files, newApexFile(ctx, apexAconfigFile, "aconfig_flags", "etc", etc, android.ModuleProxy{}))

  storageFilesVersion := ctx.Config().ReleaseAconfigStorageVersion()
  for _, info := range createStorageInfo {
   outputFile := android.PathForModuleOut(ctx, info.Output_file)
   ctx.Build(pctx, android.BuildParams{
    Rule:        aconfig.CreateStorageRule,
    Inputs:      aconfigFiles,
    Output:      outputFile,
    Description: info.Desc,
    Args: map[string]string{
     "container":   ctx.ModuleName(),
     "file_type":   info.File_type,
     "cache_files": android.JoinPathsWithPrefix(aconfigFiles, "--cache "),
     "version":     storageFilesVersion,
    },
   })
   files = append(files, newApexFile(ctx, outputFile, info.File_type, "etc", etc, android.ModuleProxy{}))
  }
 }
 return files
}

// buildManifest creates buile rules to modify the input apex_manifest.json to add information
// gathered by the build system such as provided/required native libraries. Two output files having
// different formats are generated. a.manifestJsonOut is JSON format for Q devices, and
// a.manifest.PbOut is protobuf format for R+ devices.
// TODO(jiyong): make this to return paths instead of directly storing the paths to apexBundle
func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, requireNativeLibs []string) {
 src := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))

 // Put dependency({provide|require}NativeLibs) in apex_manifest.json
 provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
 requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))

 // VNDK APEX name is determined at runtime, so update "name" in apex_manifest
 optCommands := []string{}
 if a.vndkApex {
  apexName := vndkApexNamePrefix + a.vndkVersion()
  optCommands = append(optCommands, "-v name "+apexName)
 }

 // Collect jniLibs. Notice that a.filesInfo is already sorted
 var jniLibs []string
 for _, fi := range a.filesInfo {
  if fi.isJniLib && !android.InList(fi.stem(), jniLibs) {
   jniLibs = append(jniLibs, fi.stem())
  }
 }
 if len(jniLibs) > 0 {
  optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " "))
 }

 if android.InList(":vndk", requireNativeLibs) {
  if _, vndkVersion := a.getImageVariationPair(); vndkVersion != "" {
   optCommands = append(optCommands, "-v vndkVersion "+vndkVersion)
  }
 }

 manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
 defaultVersion := ctx.Config().ReleaseDefaultUpdatableModuleVersion()
 if a.properties.Variant_version != nil {
  defaultVersionInt, err := strconv.Atoi(defaultVersion)
  if err != nil {
   ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to be an int, but got %s", defaultVersion)
  }
  if defaultVersionInt%10 != 0 && defaultVersionInt%10 != 9 {
   ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to end in a zero or a nine, but got %s", defaultVersion)
  }
  variantVersion := []rune(*a.properties.Variant_version)
  if len(variantVersion) != 1 || variantVersion[0] < '0' || variantVersion[0] > '9' {
   ctx.PropertyErrorf("variant_version", "expected an integer between 0-9; got %s", *a.properties.Variant_version)
  }
  defaultVersionRunes := []rune(defaultVersion)
  defaultVersionRunes[len(defaultVersion)-1] = []rune(variantVersion)[0]
  defaultVersion = string(defaultVersionRunes)
 }
 if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" {
  defaultVersion = override
 }
 ctx.Build(pctx, android.BuildParams{
  Rule:   apexManifestRule,
  Input:  src,
  Output: manifestJsonFullOut,
  Args: map[string]string{
   "provideNativeLibs": strings.Join(provideNativeLibs, " "),
   "requireNativeLibs": strings.Join(requireNativeLibs, " "),
   "default_version":   defaultVersion,
   "opt":               strings.Join(optCommands, " "),
  },
 })

 // b/143654022 Q apexd can't understand newly added keys in apex_manifest.json prepare
 // stripped-down version so that APEX modules built from R+ can be installed to Q
 minSdkVersion := a.minSdkVersion(ctx)
 if minSdkVersion.EqualTo(android.SdkVersion_Android10) {
  a.manifestJsonOut = android.PathForModuleOut(ctx, "apex_manifest.json")
  ctx.Build(pctx, android.BuildParams{
   Rule:   stripApexManifestRule,
   Input:  manifestJsonFullOut,
   Output: a.manifestJsonOut,
  })
 }

 // From R+, protobuf binary format (.pb) is the standard format for apex_manifest
 a.manifestPbOut = android.PathForModuleOut(ctx, "apex_manifest.pb")
 ctx.Build(pctx, android.BuildParams{
  Rule:   pbApexManifestRule,
  Input:  manifestJsonFullOut,
  Output: a.manifestPbOut,
 })
}

// buildFileContexts create build rules to append an entry for apex_manifest.pb to the file_contexts
// file for this APEX which is either from /systme/sepolicy/apex/<apexname>-file_contexts or from
// the file_contexts property of this APEX. This is to make sure that the manifest file is correctly
// labeled as system_file or vendor_apex_metadata_file.
func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.Path {
 var fileContexts android.Path
 var fileContextsDir string
 isFileContextsModule := false
 if a.properties.File_contexts == nil {
  fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts")
 } else {
  if m, t := android.SrcIsModuleWithTag(*a.properties.File_contexts); m != "" {
   isFileContextsModule = true
   otherModule := android.GetModuleProxyFromPathDep(ctx, m, t)
   if !otherModule.IsNil() {
    fileContextsDir = ctx.OtherModuleDir(otherModule)
   }
  }
  fileContexts = android.PathForModuleSrc(ctx, *a.properties.File_contexts)
 }
 if fileContextsDir == "" {
  fileContextsDir = filepath.Dir(fileContexts.String())
 }
 fileContextsDir += string(filepath.Separator)

 if a.Platform() {
  if !strings.HasPrefix(fileContextsDir, "system/sepolicy/") {
   ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but found in  %q", fileContextsDir)
  }
 }
 if !isFileContextsModule && !android.ExistentPathForSource(ctx, fileContexts.String()).Valid() {
  ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", fileContexts.String())
 }

 output := android.PathForModuleOut(ctx, "file_contexts")
 rule := android.NewRuleBuilder(pctx, ctx)

 labelForRoot := "u:object_r:system_file:s0"
 labelForManifest := "u:object_r:system_file:s0"
 if a.SocSpecific() && !a.vndkApex {
  // APEX on /vendor should label ./ and ./apex_manifest.pb as vendor file.
  labelForRoot = "u:object_r:vendor_file:s0"
  labelForManifest = "u:object_r:vendor_apex_metadata_file:s0"
 }
 // remove old file
 rule.Command().BuiltTool("rm").FlagWithOutput("-f ", output)
 // copy file_contexts
 rule.Command().BuiltTool("cat").Input(fileContexts).Text(">>").Output(output)
 // new line
 rule.Command().BuiltTool("echo").Text(">>").Output(output)
 // force-label /apex_manifest.pb and /
 rule.Command().BuiltTool("echo").Text("/apex_manifest\\\\.pb").Text(labelForManifest).Text(">>").Output(output)
 rule.Command().BuiltTool("echo").Text("/").Text(labelForRoot).Text(">>").Output(output)

 rule.Build("file_contexts."+a.Name(), "Generate file_contexts")
 return output
}

// buildInstalledFilesFile creates a build rule for the installed-files.txt file where the list of
// files included in this APEX is shown. The text file is dist'ed so that people can see what's
// included in the APEX without actually downloading and extracting it.
func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageZip android.Path) android.Path {
 output := android.PathForModuleOut(ctx, "installed-files.txt")
 unzippedDir := android.PathForModuleOut(ctx, "installed-files-unzipped")
 rule := android.NewRuleBuilder(pctx, ctx)
 rule.Command().
  Implicit(builtApex).
  BuiltTool("zipsync").
  FlagWithArg("-d ", unzippedDir.String()).
  Input(imageZip)
 rule.Command().
  BuiltTool("find").
  Text(unzippedDir.String()).
  Text("\\( -type f -o -type l \\) -printf \"%s ./%P\\n\" |").
  BuiltTool("sort").
  Text("-nr > ").
  Output(output)
 rule.Build("installed-files."+a.Name(), "Installed files")
 return output
}

// buildBundleConfig creates a build rule for the bundle config file that will control the bundle
// creation process.
func (a *apexBundle) buildBundleConfig(ctx android.ModuleContext) android.Path {
 output := android.PathForModuleOut(ctx, "bundle_config.json")

 type ApkConfig struct {
  Package_name string `json:"package_name"`
  Apk_path     string `json:"path"`
 }
 config := struct {
  Compression struct {
   Uncompressed_glob []string `json:"uncompressed_glob"`
  } `json:"compression"`
  Apex_config struct {
   Apex_embedded_apk_config []ApkConfig `json:"apex_embedded_apk_config,omitempty"`
  } `json:"apex_config,omitempty"`
 }{}

 config.Compression.Uncompressed_glob = []string{
  "apex_payload.img",
  "apex_manifest.*",
 }

 // Collect the manifest names and paths of android apps if their manifest names are
 // overridden.
 for _, fi := range a.filesInfo {
  if fi.class != app && fi.class != appSet {
   continue
  }
  packageName := fi.overriddenPackageName
  if packageName != "" {
   config.Apex_config.Apex_embedded_apk_config = append(
    config.Apex_config.Apex_embedded_apk_config,
    ApkConfig{
     Package_name: packageName,
     Apk_path:     fi.path(),
    })
  }
 }

 j, err := json.Marshal(config)
 if err != nil {
  panic(fmt.Errorf("error while marshalling to %q: %#v", output, err))
 }

 android.WriteFileRule(ctx, output, string(j))

 return output
}

func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android.Path) android.Path {
 return java.ManifestFixer(ctx, androidManifestFile, java.ManifestFixerParams{
  TestOnly: true,
 })
}

func shouldApplyAssembleVintf(fi apexFile) bool {
 isVintfFragment, _ := path.Match("etc/vintf/*", fi.path())
 fromVintfFragmentModule := fi.providers != nil && fi.providers.vintfFragmentInfo != nil
 return isVintfFragment && !fromVintfFragmentModule
}

func runAssembleVintf(ctx android.ModuleContext, vintfFragment android.Path) android.Path {
 processed := android.PathForModuleOut(ctx, "vintf", vintfFragment.Base())
 ctx.Build(pctx, android.BuildParams{
  Rule:        android.AssembleVintfRule,
  Input:       vintfFragment,
  Output:      processed,
  Description: "run assemble_vintf for VINTF in APEX",
 })
 return processed
}

// installApexSystemServerFiles installs dexpreopt and dexjar files for system server classpath entries
// provided by the apex.  They are installed here instead of in library module because there may be multiple
// variants of the library, generally one for the "main" apex and another with a different min_sdk_version
// for the Android Go version of the apex.  Both variants would attempt to install to the same locations,
// and the library variants cannot determine which one should.  The apex module is better equipped to determine
// if it is "selected".
// This assumes that the jars produced by different min_sdk_version values are identical, which is currently
// true but may not be true if the min_sdk_version difference between the variants spans version that changed
// the dex format.
func (a *apexBundle) installApexSystemServerFiles(ctx android.ModuleContext) {
 // If performInstalls is set this module is responsible for creating the install rules.
 performInstalls := a.GetOverriddenBy() == "" && !a.testApex && a.installable()
 // TODO(b/234351700): Remove once ART does not have separated debug APEX, or make the selection
 // explicit in the ART Android.bp files.
 if ctx.Config().UseDebugArt() {
  if ctx.ModuleName() == "com.android.art" {
   performInstalls = false
  }
 } else {
  if ctx.ModuleName() == "com.android.art.debug" {
   performInstalls = false
  }
 }

 psi := android.PrebuiltSelectionInfoMap{}
 ctx.VisitDirectDepsProxy(func(am android.ModuleProxy) {
  if info, exists := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); exists {
   psi = info
  }
 })

 if len(psi.GetSelectedModulesForApiDomain(ctx.ModuleName())) > 0 {
  performInstalls = false
 }

 for _, fi := range a.filesInfo {
  for _, install := range fi.systemServerDexpreoptInstalls {
   var installedFile android.InstallPath
   if performInstalls {
    // android_device will create the install rule in soong-only builds.
    // Skip creating the installation rule from the base variant
    // in soong-only builds to prevent duplicate installation rules.
    installedFile = ctx.InstallFile(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
   } else {
    // Another module created the install rules, but this module should still depend on
    // the installed locations.
    installedFile = install.InstallDirOnDevice.Join(ctx, install.InstallFileOnDevice)
   }
   a.extraInstalledFiles = append(a.extraInstalledFiles, installedFile)
   a.extraInstalledPairs = append(a.extraInstalledPairs, installPair{install.OutputPathOnHost, installedFile})
   ctx.PackageFileWithFakeFullInstall(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
  }
  if performInstalls {
   for _, dexJar := range fi.systemServerDexJars {
    // Copy the system server dex jar to a predefined location where dex2oat will find it.
    android.CopyFileRule(ctx, dexJar,
     android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir, dexJar.Base()))
   }
  }
 }
}

// buildApex creates build rules to build an APEX using apexer.
func (a *apexBundle) buildApex(ctx android.ModuleContext) {
 suffix := imageApexSuffix
 apexName := a.BaseModuleName()

 ////////////////////////////////////////////////////////////////////////////////////////////
 // Step 1: copy built files to appropriate directories under the image directory

 imageDir := android.PathForModuleOut(ctx, "image"+suffix)

 installSymbolFiles := a.ExportedToMake() && a.installable()

 // set of dependency module:location mappings
 installMapSet := make(map[string]bool)

 // TODO(jiyong): use the RuleBuilder
 var copyCommands []string
 var implicitInputs []android.Path
 rmCmd, _, _ := android.Rm.GetValueAndDeps(ctx.Config())
 mkdirCmd, _, _ := android.Mkdir.GetValueAndDeps(ctx.Config())
 lnCmd, _, _ := android.Ln.GetValueAndDeps(ctx.Config())
 cpCmd, _, _ := android.Cp.GetValueAndDeps(ctx.Config())
 zipSyncCmd, _, _ := android.ZipSync.GetValueAndDeps(ctx.Config())
 apexDir := android.PathForModuleInPartitionInstall(ctx, "apex", apexName)
 for _, fi := range a.filesInfo {
  destPath := imageDir.Join(ctx, fi.path()).String()
  // Prepare the destination path
  destPathDir := filepath.Dir(destPath)
  if fi.class == appSet {
   copyCommands = append(copyCommands, rmCmd+" -rf "+destPathDir)
  }
  copyCommands = append(copyCommands, mkdirCmd+" -p "+destPathDir)

  installMapPath := fi.builtFile

  // Copy the built file to the directory. But if the symlink optimization is turned
  // on, place a symlink to the corresponding file in /system partition instead.
  if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
   pathOnDevice := filepath.Join("/", fi.partition, fi.path())
   copyCommands = append(copyCommands, lnCmd+" -sfn "+pathOnDevice+" "+destPath)
  } else {
   // Copy the file into APEX
   if !a.testApex && shouldApplyAssembleVintf(fi) {
    // copy the output of assemble_vintf instead of the original
    vintfFragment := runAssembleVintf(ctx, fi.builtFile)
    copyCommands = append(copyCommands, cpCmd+" -f "+vintfFragment.String()+" "+destPath)
    implicitInputs = append(implicitInputs, vintfFragment)
   } else {
    copyCommands = append(copyCommands, cpCmd+" -f "+fi.builtFile.String()+" "+destPath)
    implicitInputs = append(implicitInputs, fi.builtFile)
   }
   if fi.extraZip.Valid() {
    copyCommands = append(copyCommands,
     fmt.Sprintf("%s -d %s %s", zipSyncCmd, destPathDir, fi.extraZip.String()))
    implicitInputs = append(implicitInputs, fi.extraZip.Path())
   }

   var installedPath android.InstallPath
   if installSymbolFiles {
    if fi.extraZip.Valid() {
     installedPath = ctx.InstallFileWithExtraFilesZip(apexDir.Join(ctx, fi.installDir),
      fi.stem(), fi.builtFile, fi.extraZip.Path())
    } else {
     // store installedPath. symlinks might be created if required.
     installedPath = ctx.InstallFile(apexDir.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
    }
   }

   // Create additional symlinks pointing the file inside the APEX (if any). Note that
   // this is independent from the symlink optimization.
   for _, symlinkPath := range fi.symlinkPaths() {
    symlinkDest := imageDir.Join(ctx, symlinkPath).String()
    copyCommands = append(copyCommands, lnCmd+" -sfn "+filepath.Base(destPath)+" "+symlinkDest)
    if installSymbolFiles {
     ctx.InstallSymlink(apexDir.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
    }
   }

   installMapPath = installedPath
  }

  // Copy the test files (if any)
  for _, d := range fi.dataPaths {
   // TODO(eakammer): This is now the third repetition of ~this logic for test paths, refactoring should be possible
   relPath := d.ToRelativeInstallPath()
   dataDest := imageDir.Join(ctx, fi.apexRelativePath(relPath)).String()

   copyCommands = append(copyCommands, cpCmd+" -f "+d.SrcPath.String()+" "+dataDest)
   implicitInputs = append(implicitInputs, d.SrcPath)
  }

  installMapSet[installMapPath.String()+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
 }

 implicitInputs = append(implicitInputs, a.manifestPbOut)

 if len(installMapSet) > 0 {
  var installs []string
  installs = append(installs, android.SortedKeys(installMapSet)...)
  ctx.SetLicenseInstallMap(installs)
 }

 ////////////////////////////////////////////////////////////////////////////////////////////
 // Step 1.a: Write the list of files in this APEX to a txt file and compare it against
 // the allowed list given via the allowed_files property. Build fails when the two lists
 // differ.
 //
 // TODO(jiyong): consider removing this. Nobody other than com.android.apex.cts.shim.* seems
 // to be using this at this moment. Furthermore, this looks very similar to what
 // buildInstalledFilesFile does. At least, move this to somewhere else so that this doesn't
 // hurt readability.
 if a.overridableProperties.Allowed_files != nil {
  // Build content.txt
  var contentLines []string
  imageContentFile := android.PathForModuleOut(ctx, "content.txt")
  contentLines = append(contentLines, "./apex_manifest.pb")
  minSdkVersion := a.minSdkVersion(ctx)
  if minSdkVersion.EqualTo(android.SdkVersion_Android10) {
   contentLines = append(contentLines, "./apex_manifest.json")
  }
  for _, fi := range a.filesInfo {
   contentLines = append(contentLines, "./"+fi.path())
  }
  sort.Strings(contentLines)
  android.WriteFileRule(ctx, imageContentFile, strings.Join(contentLines, "\n"))
  implicitInputs = append(implicitInputs, imageContentFile)

  // Compare content.txt against allowed_files.
  allowedFilesFile := android.PathForModuleSrc(ctx, proptools.String(a.overridableProperties.Allowed_files))
  phonyOutput := android.PathForModuleOut(ctx, a.Name()+"-diff-phony-output")
  ctx.Build(pctx, android.BuildParams{
   Rule:        diffApexContentRule,
   Implicits:   implicitInputs,
   Output:      phonyOutput,
   Description: "diff apex image content",
   Args: map[string]string{
    "allowed_files_file": allowedFilesFile.String(),
    "image_content_file": imageContentFile.String(),
    "apex_module_name":   a.Name(),
   },
  })
  implicitInputs = append(implicitInputs, phonyOutput)
 }

 unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned")
 outHostBinDir := ctx.Config().HostToolPath(ctx, "").String()
 prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")

 ////////////////////////////////////////////////////////////////////////////////////
 // Step 2: create canned_fs_config which encodes filemode,uid,gid of each files
 // in this APEX. The file will be used by apexer in later steps.
 cannedFsConfig := a.buildCannedFsConfig(ctx)
 implicitInputs = append(implicitInputs, cannedFsConfig)

 ////////////////////////////////////////////////////////////////////////////////////
 // Step 3: Prepare option flags for apexer and invoke it to create an unsigned APEX.
 // TODO(jiyong): use the RuleBuilder
 optFlags := []string{}

 fileContexts := a.buildFileContexts(ctx)
 implicitInputs = append(implicitInputs, fileContexts)

 implicitInputs = append(implicitInputs, a.privateKeyFile, a.publicKeyFile)
 optFlags = append(optFlags, "--pubkey "+a.publicKeyFile.String())

 manifestPackageName := a.getOverrideManifestPackageName(ctx)
 if manifestPackageName != "" {
  optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName)
 }

 androidManifest := a.properties.AndroidManifest.GetOrDefault(ctx, "")
 if androidManifest != "" {
  androidManifestFile := android.PathForModuleSrc(ctx, androidManifest)

  if a.testApex {
   androidManifestFile = markManifestTestOnly(ctx, androidManifestFile)
  }

  implicitInputs = append(implicitInputs, androidManifestFile)
  optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
 } else if a.testApex {
  optFlags = append(optFlags, "--test_only")
 }

 // Determine target/min sdk version from the context
 // TODO(jiyong): make this as a function
 moduleMinSdkVersion := a.minSdkVersion(ctx)
 minSdkVersion := moduleMinSdkVersion.String()

 // bundletool doesn't understand what "current" is. We need to transform it to
 // codename
 if moduleMinSdkVersion.IsCurrent() || moduleMinSdkVersion.IsNone() {
  minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()

  if useApiFingerprint, fingerprintMinSdkVersion, fingerprintDeps :=
   java.UseApiFingerprint(ctx); useApiFingerprint {
   minSdkVersion = fingerprintMinSdkVersion
   implicitInputs = append(implicitInputs, fingerprintDeps)
  }
 }
 // apex module doesn't have a concept of target_sdk_version, hence for the time
 // being targetSdkVersion == default targetSdkVersion of the branch.
 targetSdkVersion := strconv.Itoa(ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt())

 if useApiFingerprint, fingerprintTargetSdkVersion, fingerprintDeps :=
  java.UseApiFingerprint(ctx); useApiFingerprint {
  targetSdkVersion = fingerprintTargetSdkVersion
  implicitInputs = append(implicitInputs, fingerprintDeps)
 }
 optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion)
 optFlags = append(optFlags, "--min_sdk_version "+minSdkVersion)

 if a.overridableProperties.Logging_parent != "" {
  optFlags = append(optFlags, "--logging_parent ", a.overridableProperties.Logging_parent)
 }

 // Create a NOTICE file, and embed it as an asset file in the APEX.
 htmlGzNotice := android.PathForModuleOut(ctx, "NOTICE.html.gz")
 android.BuildNoticeHtmlOutputFromLicenseMetadata(
  ctx, htmlGzNotice, "", "",
  android.BuildNoticeFromLicenseDataArgs{
   StripPrefix: []string{
    android.PathForModuleInstall(ctx).String() + "/",
    android.PathForModuleInPartitionInstall(ctx, "apex").String() + "/",
   },
  },
  ctx.ModuleProxy(),
 )
 noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
 builder := android.NewRuleBuilder(pctx, ctx)
 builder.Command().BuiltTool("cp").
  Input(htmlGzNotice).
  Output(noticeAssetPath)
 builder.Build("notice_dir", "Building notice dir")
 implicitInputs = append(implicitInputs, noticeAssetPath)
 optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeAssetPath.String()))

 if a.testOnlyShouldSkipPayloadSign() {
  optFlags = append(optFlags, "--unsigned_payload")
 }

 if moduleMinSdkVersion == android.SdkVersion_Android10 {
  implicitInputs = append(implicitInputs, a.manifestJsonOut)
  optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
 }

 optFlags = append(optFlags, "--payload_fs_type "+a.payloadFsType.string())

 if a.payloadFsType == erofs {
  args, inputs := a.buildApexerArgsForErofs(ctx)
  optFlags = append(optFlags, args...)
  implicitInputs = append(implicitInputs, inputs...)
 }

 if a.nonProduction() {
  optFlags = append(optFlags, "--non_production")
 }

 imageZipOut := android.PathForModuleOut(ctx, "image.zip")
 if a.dynamic_common_lib_apex() {
  ctx.Build(pctx, android.BuildParams{
   Rule:           DCLAApexRule,
   Implicits:      implicitInputs,
   Output:         unsignedOutputFile,
   ImplicitOutput: imageZipOut,
   Description:    "apex",
   Args: map[string]string{
    "tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
    "image_dir":        imageDir.String(),
    "image_zip":        imageZipOut.String(),
    "copy_commands":    strings.Join(copyCommands, " && "),
    "manifest":         a.manifestPbOut.String(),
    "file_contexts":    fileContexts.String(),
    "canned_fs_config": cannedFsConfig.String(),
    "key":              a.privateKeyFile.String(),
    "opt_flags":        strings.Join(optFlags, " "),
   },
  })
 } else {
  ctx.Build(pctx, android.BuildParams{
   Rule:           apexRule,
   Implicits:      implicitInputs,
   Output:         unsignedOutputFile,
   ImplicitOutput: imageZipOut,
   Description:    "apex",
   Args: map[string]string{
    "tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
    "image_dir":        imageDir.String(),
    "image_zip":        imageZipOut.String(),
    "copy_commands":    strings.Join(copyCommands, " && "),
    "manifest":         a.manifestPbOut.String(),
    "file_contexts":    fileContexts.String(),
    "canned_fs_config": cannedFsConfig.String(),
    "key":              a.privateKeyFile.String(),
    "opt_flags":        strings.Join(optFlags, " "),
   },
  })
 }

 // TODO(jiyong): make the two rules below as separate functions
 apexProtoFile := android.PathForModuleOut(ctx, a.Name()+".pb"+suffix)
 bundleModuleFile := android.PathForModuleOut(ctx, a.Name()+suffix+"-base.zip")
 a.bundleModuleFile = bundleModuleFile

 ctx.Build(pctx, android.BuildParams{
  Rule:        apexProtoConvertRule,
  Input:       unsignedOutputFile,
  Output:      apexProtoFile,
  Description: "apex proto convert",
 })

 implicitInputs = append(implicitInputs, unsignedOutputFile)

 // Run coverage analysis
 apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.txt")
 ctx.Build(pctx, android.BuildParams{
  Rule:        generateAPIsUsedbyApexRule,
  Input:       imageZipOut,
  Implicits:   implicitInputs,
  Description: "coverage",
  Output:      apisUsedbyOutputFile,
  Args: map[string]string{
   "image_zip": imageZipOut.String(),
  },
 })

 var nativeLibNames []string
 for _, f := range a.filesInfo {
  if f.class == nativeSharedLib {
   nativeLibNames = append(nativeLibNames, f.stem())
  }
 }
 apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt")
 rb := android.NewRuleBuilder(pctx, ctx)
 rb.Command().
  BuiltTool("gen_apex_symbols").
  Text("ndk_backedby").
  Output(apisBackedbyOutputFile).
  Flags(nativeLibNames)
 rb.Build("ndk_backedby_list", "Generate API libraries backed by Apex")

 var javaLibOrApkPath []android.Path
 for _, f := range a.filesInfo {
  if f.class == javaSharedLib || f.class == app {
   javaLibOrApkPath = append(javaLibOrApkPath, f.builtFile)
  }
 }
 javaApiUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.xml")
 javaUsedByRule := android.NewRuleBuilder(pctx, ctx)
 javaUsedByRule.Command().
  BuiltTool("gen_apex_symbols").
  Text("java_usedby").
  BuiltTool("dexdeps").
  Output(javaApiUsedbyOutputFile).
  Inputs(javaLibOrApkPath)
 javaUsedByRule.Build("java_usedby_list", "Generate Java APIs used by Apex")

 if slices.Contains(ctx.Config().UnbundledBuildApps(), a.Name()) && !android.ShouldSkipAndroidMkProcessing(ctx, a) {
  ctx.DistForGoalWithFilename("apps_only", apisUsedbyOutputFile, "ndk_apis_usedby_apex/"+apisUsedbyOutputFile.Base())
  ctx.DistForGoalWithFilename("apps_only", apisBackedbyOutputFile, "ndk_apis_backedby_apex/"+apisBackedbyOutputFile.Base())
  ctx.DistForGoalWithFilename("apps_only", javaApiUsedbyOutputFile, "java_apis_used_by_apex/"+javaApiUsedbyOutputFile.Base())
 }

 bundleConfig := a.buildBundleConfig(ctx)

 var abis []string
 for _, target := range ctx.MultiTargets() {
  if len(target.Arch.Abi) > 0 {
   abis = append(abis, target.Arch.Abi[0])
  }
 }

 abis = android.FirstUniqueStrings(abis)

 ctx.Build(pctx, android.BuildParams{
  Rule:        apexBundleRule,
  Input:       apexProtoFile,
  Implicit:    bundleConfig,
  Output:      a.bundleModuleFile,
  Description: "apex bundle module",
  Args: map[string]string{
   "abi":    strings.Join(abis, "."),
   "config": bundleConfig.String(),
  },
 })

 ////////////////////////////////////////////////////////////////////////////////////
 // Step 4: Sign the APEX using signapk
 signedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix)

 pem, key := a.getCertificateAndPrivateKey(ctx)
 rule := java.Signapk
 args := map[string]string{
  "certificates": pem.String() + " " + key.String(),
  "flags":        "--disable-v1 -a 4096 --align-file-size", //alignment
 }
 implicits := android.Paths{pem, key}
 if ctx.Config().UseREWrapper() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
  rule = java.SignapkRE
  args["implicits"] = strings.Join(implicits.Strings(), ",")
  args["outCommaList"] = signedOutputFile.String()
 }
 var validations android.Paths
 validations = append(validations, runApexLinkerconfigValidation(ctx, signedOutputFile, imageZipOut))
 if !a.skipValidation(apexSepolicyTests) && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
  validations = append(validations, runApexSepolicyTests(ctx, a, signedOutputFile))
 }
 if !a.testApex && len(a.properties.Unwanted_transitive_deps) > 0 {
  validations = append(validations,
   runApexElfCheckerUnwanted(ctx, signedOutputFile, a.properties.Unwanted_transitive_deps))
 }
 if !a.skipValidation(hostApexVerifier) && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
  validations = append(validations, runApexHostVerifier(ctx, a, signedOutputFile))
 }
 ctx.Build(pctx, android.BuildParams{
  Rule:        rule,
  Description: "signapk",
  Output:      signedOutputFile,
  Input:       unsignedOutputFile,
  Implicits:   implicits,
  Args:        args,
  Validations: validations,
 })
 if suffix == imageApexSuffix {
  a.outputApexFile = signedOutputFile
 }
 a.outputFile = signedOutputFile

 if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() {
  ctx.PropertyErrorf("test_only_force_compression", "not available")
  return
 }

 installSuffix := suffix
 a.setCompression(ctx)
 if a.isCompressed {
  unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix+".unsigned")

  compressRule := android.NewRuleBuilder(pctx, ctx)
  compressRule.Command().
   BuiltTool("rm").
   FlagWithOutput("-f ", unsignedCompressedOutputFile)
  compressRule.Command().
   BuiltTool("apex_compression_tool").
   Flag("compress").
   FlagWithArg("--apex_compression_tool ", outHostBinDir+":"+prebuiltSdkToolsBinDir).
   FlagWithInput("--input ", signedOutputFile).
   FlagWithOutput("--output ", unsignedCompressedOutputFile)
  compressRule.Build("compressRule", "Generate unsigned compressed APEX file")

  signedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix)
  if ctx.Config().UseREWrapper() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
   args["outCommaList"] = signedCompressedOutputFile.String()
  }
  ctx.Build(pctx, android.BuildParams{
   Rule:        rule,
   Description: "sign compressedApex",
   Output:      signedCompressedOutputFile,
   Input:       unsignedCompressedOutputFile,
   Implicits:   implicits,
   Args:        args,
  })
  a.outputFile = signedCompressedOutputFile
  installSuffix = imageCapexSuffix
 }

 if !a.installable() {
  a.SkipInstall()
 }

 installDeps := slices.Concat(a.compatSymlinks, a.extraInstalledFiles)
 // Install to $OUT/soong/{target,host}/.../apex.
 a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile, installDeps...)

 // installed-files.txt is dist'ed
 a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageZipOut)

 a.apexKeysPath = writeApexKeys(ctx, a)
}

// getCertificateAndPrivateKey retrieves the cert and the private key that will be used to sign
// the zip container of this APEX. See the description of the 'certificate' property for how
// the cert and the private key are found.
func (a *apexBundle) getCertificateAndPrivateKey(ctx android.PathContext) (pem, key android.Path) {
 if a.containerCertificateFile != nil {
  return a.containerCertificateFile, a.containerPrivateKeyFile
 }

 cert := String(a.overridableProperties.Certificate)
 if cert == "" {
  return ctx.Config().DefaultAppCertificate(ctx)
 }

 defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
 pem = defaultDir.Join(ctx, cert+".x509.pem")
 key = defaultDir.Join(ctx, cert+".pk8")
 return pem, key
}

func (a *apexBundle) getOverrideManifestPackageName(ctx android.ModuleContext) string {
 // For VNDK APEXes, check "com.android.vndk" in PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
 // to see if it should be overridden because their <apex name> is dynamically generated
 // according to its VNDK version.
 if a.vndkApex {
  overrideName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(vndkApexName)
  if overridden {
   return overrideName + ".v" + a.vndkVersion()
  }
  return ""
 }
 packageNameFromProp := a.overridableProperties.Package_name.GetOrDefault(ctx, "")
 if packageNameFromProp != "" {
  return packageNameFromProp
 }
 manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
 if overridden {
  return manifestPackageName
 }
 return ""
}

func (a *apexBundle) buildApexDependencyInfo(ctx android.ModuleContext) {
 depInfos := android.DepNameToDepInfoMap{}
 a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from, to android.ModuleProxy, externalDep bool) bool {
  if from.Name() == to.Name() {
   // This can happen for cc.reuseObjTag. We are not interested in tracking this.
   // As soon as the dependency graph crosses the APEX boundary, don't go further.
   return !externalDep
  }

  // Skip dependencies that are only available to APEXes; they are developed with updatability
  // in mind and don't need manual approval.
  if android.OtherModuleProviderOrDefault(ctx, to, android.PlatformAvailabilityInfoProvider).NotAvailableToPlatform {
   return !externalDep
  }

  depTag := ctx.OtherModuleDependencyTag(to)
  // Check to see if dependency been marked to skip the dependency check
  if skipDepCheck, ok := depTag.(android.SkipApexAllowedDependenciesCheck); ok && skipDepCheck.SkipApexAllowedDependenciesCheck() {
   return !externalDep
  }

  if info, exists := depInfos[to.Name()]; exists {
   if !android.InList(from.Name(), info.From) {
    info.From = append(info.From, from.Name())
   }
   info.IsExternal = info.IsExternal && externalDep
   depInfos[to.Name()] = info
  } else {
   toMinSdkVersion := "(no version)"
   if info, ok := android.OtherModuleProvider(ctx, to, android.CommonModuleInfoProvider); ok &&
    !info.MinSdkVersion.IsPlatform && info.MinSdkVersion.ApiLevel != nil {
    toMinSdkVersion = info.MinSdkVersion.ApiLevel.String()
   }
   depInfos[to.Name()] = android.ApexModuleDepInfo{
    To:            to.Name(),
    From:          []string{from.Name()},
    IsExternal:    externalDep,
    MinSdkVersion: toMinSdkVersion,
   }
  }

  // As soon as the dependency graph crosses the APEX boundary, don't go further.
  return !externalDep
 })

 a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).String(), depInfos)

 ctx.Phony(a.Name()+"-deps-info", a.ApexBundleDepsInfo.FullListPath(), a.ApexBundleDepsInfo.FlatListPath())
}

func (a *apexBundle) buildLintReports(ctx android.ModuleContext) {
 depSetsBuilder := java.NewLintDepSetBuilder()
 for _, fi := range a.filesInfo {
  if fi.lintInfo != nil {
   depSetsBuilder.Transitive(fi.lintInfo)
  }
 }

 depSets := depSetsBuilder.Build()
 var validations android.Paths

 if a.checkStrictUpdatabilityLinting(ctx) {
  baselines := depSets.Baseline.ToList()
  if len(baselines) > 0 {
   outputFile := java.VerifyStrictUpdatabilityChecks(ctx, baselines)
   validations = append(validations, outputFile)
  }
 }

 a.lintReports = java.BuildModuleLintReportZips(ctx, depSets, validations)
}

func (a *apexBundle) reexportJacocoInfo(ctx android.ModuleContext) {
 var jacocoInfos []java.JacocoInfo
 for _, fi := range a.filesInfo {
  if fi.jacocoInfo.ReportClassesFile != nil {
   jacocoInfos = append(jacocoInfos, fi.jacocoInfo)
  }
 }

 android.SetProvider(ctx, java.ApexJacocoInfoProvider, jacocoInfos)
 android.SetProvider(ctx, java.BundleProvider, java.BundleInfo{Bundle: a.bundleModuleFile})
}

func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.Path {
 var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
 var executablePaths []string // this also includes dirs
 var zipDirs []string
 zipFiles := make(map[string]android.Path)
 for _, f := range a.filesInfo {
  pathInApex := f.path()
  if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
   executablePaths = append(executablePaths, pathInApex)
   for _, d := range f.dataPaths {
    rel := d.ToRelativeInstallPath()
    readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, rel))
   }
   for _, s := range f.symlinks {
    executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
   }
  } else {
   readOnlyPaths = append(readOnlyPaths, pathInApex)
  }
  if f.extraZip.Valid() {
   zipDirs = append(zipDirs, f.installDir)
   zipFiles[f.installDir] = f.extraZip.Path()
  }
  dir := f.installDir
  for !android.InList(dir, executablePaths) && dir != "" {
   executablePaths = append(executablePaths, dir)
   dir, _ = filepath.Split(dir) // move up to the parent
   if len(dir) > 0 {
    // remove trailing slash
    dir = dir[:len(dir)-1]
   }
  }
 }
 sort.Strings(readOnlyPaths)
 sort.Strings(executablePaths)
 sort.Strings(zipDirs)

 cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
 builder := android.NewRuleBuilder(pctx, ctx)
 cmd := builder.Command()
 cmd.Text("(")
 cmd.BuiltTool("echo").Text("'/ 1000 1000 0755';")
 for _, p := range readOnlyPaths {
  cmd.BuiltTool("echo").Textf("'/%s 1000 1000 0644';", p)
 }
 for _, p := range executablePaths {
  cmd.BuiltTool("echo").Textf("'/%s 0 2000 0755';", p)
 }
 for _, dir := range zipDirs {
  cmd.BuiltTool("echo").Textf("'/%s 0 2000 0755';", dir)
  file := zipFiles[dir]
  cmd.PrebuiltBuildTool(ctx, "ziptool").Text("zipinfo -1").Input(file).Text(`|`).BuiltTool("sed").Textf(`"s:\(.*\):/%s/\1 1000 1000 0644:";`, dir)
 }
 // Custom fs_config is "appended" to the last so that entries from the file are preferred
 // over default ones set above.
 customFsConfig := a.properties.Canned_fs_config.GetOrDefault(ctx, "")
 if customFsConfig != "" {
  cmd.BuiltTool("cat").Input(android.PathForModuleSrc(ctx, customFsConfig))
 }
 cmd.Text(")").FlagWithOutput("> ", cannedFsConfig)
 builder.Build("generateFsConfig", fmt.Sprintf("Generating canned fs config for %s", a.BaseModuleName()))

 return cannedFsConfig
}

func runApexLinkerconfigValidation(ctx android.ModuleContext, apexFile android.Path, imageZip android.Path) android.Path {
 timestamp := android.PathForModuleOut(ctx, "apex_linkerconfig_validation.timestamp")
 ctx.Build(pctx, android.BuildParams{
  Rule:     apexLinkerconfigValidationRule,
  Input:    apexFile,
  Output:   timestamp,
  Implicit: imageZip,
  Args: map[string]string{
   "image_zip": imageZip.String(),
  },
 })
 return timestamp
}

// buildApexerArgsForErofs returns the erofs specific arguments for apexer
func (a *apexBundle) buildApexerArgsForErofs(ctx android.ModuleContext) ([]string, android.Paths) {
 var args []string
 var inputs android.Paths

 compressor := ctx.Config().DefaultApexPayloadErofsCompressor()
 if a.properties.Erofs.Compressor != nil {
  compressor = *a.properties.Erofs.Compressor
 }
 if compressor != "" {
  args = append(args, "--erofs_compressor", compressor)
 }

 var hints android.Path
 hints = ctx.Config().DefaultApexPayloadErofsCompressHints(ctx)
 if a.properties.Erofs.Compress_hints != nil {
  hints = android.PathForModuleSrc(ctx, *a.properties.Erofs.Compress_hints)
 }
 if hints != nil {
  args = append(args, "--erofs_compress_hints", hints.String())
  inputs = append(inputs, hints)
 }

 pcluster_size := ctx.Config().DefaultApexPayloadErofsPclusterSize()
 if a.properties.Erofs.Pcluster_size != nil {
  pcluster_size = *a.properties.Erofs.Pcluster_size
 }
 if pcluster_size != 0 {
  args = append(args, "--erofs_pcluster_size", strconv.FormatInt(pcluster_size, 10))
 }
 return args, inputs
}

// Can't use PartitionTag() because PartitionTag() returns the partition this module is actually
// installed (e.g. PartitionTag() may return "system" for vendor apex when vendor is linked to /system/vendor)
func (a *apexBundle) partition() string {
 if a.SocSpecific() {
  return "vendor"
 } else if a.DeviceSpecific() {
  return "odm"
 } else if a.ProductSpecific() {
  return "product"
 } else if a.SystemExtSpecific() {
  return "system_ext"
 } else {
  return "system"
 }
}

// Runs apex_sepolicy_tests
//
// $ apex-ls -Z {apex_file} > {file_contexts}
// $ apex_sepolicy_tests -f {file_contexts}
func runApexSepolicyTests(ctx android.ModuleContext, a *apexBundle, apexFile android.Path) android.Path {
 timestamp := android.PathForModuleOut(ctx, "apex_sepolicy_tests.timestamp")
 ctx.Build(pctx, android.BuildParams{
  Rule:   apexSepolicyTestsRule,
  Input:  apexFile,
  Output: timestamp,
  Args: map[string]string{
   "partition_tag": a.partition(),
  },
 })
 return timestamp
}

func runApexElfCheckerUnwanted(ctx android.ModuleContext, apexFile android.Path, unwanted []string) android.Path {
 timestamp := android.PathForModuleOut(ctx, "apex_elf_unwanted.timestamp")
 ctx.Build(pctx, android.BuildParams{
  Rule:   apexElfCheckerUnwantedRule,
  Input:  apexFile,
  Output: timestamp,
  Args: map[string]string{
   "unwanted":  android.JoinWithSuffixAndSeparator(unwanted, ".so", ":"),
   "tool_path": ctx.Config().HostToolPath(ctx, "").String() + ":${config.ClangBin}",
  },
 })
 return timestamp
}

func runApexHostVerifier(ctx android.ModuleContext, a *apexBundle, apexFile android.Path) android.Path {
 timestamp := android.PathForModuleOut(ctx, "host_apex_verifier.timestamp")
 ctx.Build(pctx, android.BuildParams{
  Rule:   apexHostVerifierRule,
  Input:  apexFile,
  Output: timestamp,
  Args: map[string]string{
   "partition_tag": a.partition(),
  },
 })
 return timestamp
}

[Dauer der Verarbeitung: 0.31 Sekunden, vorverarbeitet 2026-06-28]