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

Quelle  hiddenapi_modular.go   Sprache: unbekannt

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

// Copyright (C) 2021 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 java

import (
 "fmt"
 "strings"

 "android/soong/android"
 "android/soong/dexpreopt"

 "github.com/google/blueprint"
)

//go:generate go run ../../blueprint/gobtools/codegen

// Contains support for processing hiddenAPI in a modular fashion.

// HiddenAPIScope encapsulates all the information that the hidden API processing needs about API
// scopes, i.e. what is called android.SdkKind and apiScope. It does not just use those as they do
// not provide the information needed by hidden API processing.
// @auto-generate: gob
type HiddenAPIScope struct {
 // The name of the scope, used for debug purposes.
 name string

 // The corresponding android.SdkKind, used for retrieving paths from java_sdk_library* modules.
 sdkKind android.SdkKind

 // The option needed to passed to "hiddenapi list".
 hiddenAPIListOption string

 // The names of the source stub library modules that contain the API provided by the platform,
 // i.e. by modules that are not in an APEX.
 nonUpdatableSourceModule string

 // The names of from-text stub library modules that contain the API provided by the platform,
 // i.e. by modules that are not in an APEX.
 nonUpdatableFromTextModule string

 // The names of the prebuilt stub library modules that contain the API provided by the platform,
 // i.e. by modules that are not in an APEX.
 nonUpdatablePrebuiltModule string
}

// initHiddenAPIScope initializes the scope.
func initHiddenAPIScope(apiScope *HiddenAPIScope) *HiddenAPIScope {
 sdkKind := apiScope.sdkKind
 // The platform does not provide a core platform API.
 if sdkKind != android.SdkCorePlatform {
  kindAsString := sdkKind.String()
  var insert string
  if sdkKind == android.SdkPublic {
   insert = ""
  } else {
   insert = "." + strings.ReplaceAll(kindAsString, "-", "_")
  }

  nonUpdatableModule := "android-non-updatable"

  // Construct the name of the android-non-updatable source module for this scope.
  apiScope.nonUpdatableSourceModule = fmt.Sprintf("%s.stubs%s", nonUpdatableModule, insert)

  prebuiltModuleName := func(name string, kind string) string {
   return fmt.Sprintf("sdk_%s_current_%s", kind, name)
  }

  // Construct the name of the android-non-updatable prebuilt module for this scope.
  apiScope.nonUpdatablePrebuiltModule = prebuiltModuleName(nonUpdatableModule, kindAsString)
 }

 return apiScope
}

// android-non-updatable takes the name of a module and returns a possibly scope specific name of
// the module.
func (l *HiddenAPIScope) scopeSpecificStubModule(ctx android.BaseModuleContext, name string) string {
 // The android-non-updatable is not a java_sdk_library but there are separate stub libraries for
 // each scope.
 // TODO(b/192067200): Remove special handling of android-non-updatable.
 if name == "android-non-updatable" {
  if ctx.Config().AlwaysUsePrebuiltSdks() {
   return l.nonUpdatablePrebuiltModule
  } else {
   if l.nonUpdatableFromTextModule != "" && ctx.Config().BuildFromTextStub() {
    return l.nonUpdatableFromTextModule
   }
   return l.nonUpdatableSourceModule
  }
 } else {
  // Assume that the module is either a java_sdk_library (or equivalent) and so will provide
  // separate stub jars for each scope or is a java_library (or equivalent) in which case it will
  // have the same stub jar for each scope.
  return name
 }
}

func (l *HiddenAPIScope) String() string {
 return fmt.Sprintf("HiddenAPIScope{%s}", l.name)
}

var (
 PublicHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
  name:                "public",
  sdkKind:             android.SdkPublic,
  hiddenAPIListOption: "--public-stub-classpath",
 })
 SystemHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
  name:                "system",
  sdkKind:             android.SdkSystem,
  hiddenAPIListOption: "--system-stub-classpath",
 })
 TestHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
  name:                "test",
  sdkKind:             android.SdkTest,
  hiddenAPIListOption: "--test-stub-classpath",
 })
 ModuleLibHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
  name:                       "module-lib",
  sdkKind:                    android.SdkModule,
  nonUpdatableFromTextModule: "android-non-updatable.stubs.test_module_lib",
 })
 CorePlatformHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
  name:                "core-platform",
  sdkKind:             android.SdkCorePlatform,
  hiddenAPIListOption: "--core-platform-stub-classpath",
 })

 // hiddenAPIRelevantSdkKinds lists all the android.SdkKind instances that are needed by the hidden
 // API processing.
 //
 // These are roughly in order from narrowest API surface to widest. Widest means the API stubs
 // with the biggest API surface, e.g. test is wider than system is wider than public.
 //
 // Core platform is considered wider than system/module-lib because those modules that provide
 // core platform APIs either do not have any system/module-lib APIs at all, or if they do it is
 // because the core platform API is being converted to system/module-lib APIs. In either case the
 // system/module-lib APIs are subsets of the core platform API.
 //
 // This is not strictly in order from narrowest to widest as the Test API is wider than system but
 // is neither wider or narrower than the module-lib or core platform APIs. However, this works
 // well enough at the moment.
 // TODO(b/191644675): Correctly reflect the sub/superset relationships between APIs.
 hiddenAPIScopes = []*HiddenAPIScope{
  PublicHiddenAPIScope,
  SystemHiddenAPIScope,
  TestHiddenAPIScope,
  ModuleLibHiddenAPIScope,
  CorePlatformHiddenAPIScope,
 }

 // The HiddenAPIScope instances that are supported by a java_sdk_library.
 //
 // CorePlatformHiddenAPIScope is not used as the java_sdk_library does not have special support
 // for core_platform API, instead it is implemented as a customized form of PublicHiddenAPIScope.
 hiddenAPISdkLibrarySupportedScopes = []*HiddenAPIScope{
  PublicHiddenAPIScope,
  SystemHiddenAPIScope,
  TestHiddenAPIScope,
  ModuleLibHiddenAPIScope,
 }

 // The HiddenAPIScope instances that are supported by the `hiddenapi list`.
 hiddenAPIFlagScopes = []*HiddenAPIScope{
  PublicHiddenAPIScope,
  SystemHiddenAPIScope,
  TestHiddenAPIScope,
  CorePlatformHiddenAPIScope,
 }
)

type hiddenAPIStubsDependencyTag struct {
 blueprint.BaseDependencyTag

 // The api scope for which this dependency was added.
 apiScope *HiddenAPIScope

 // Indicates that the dependency is not for an API provided by the current bootclasspath fragment
 // but is an additional API provided by a module that is not part of the current bootclasspath
 // fragment.
 fromAdditionalDependency bool
}

func (b hiddenAPIStubsDependencyTag) ExcludeFromApexContents() {
}

func (b hiddenAPIStubsDependencyTag) ReplaceSourceWithPrebuilt() bool {
 return false
}

func (b hiddenAPIStubsDependencyTag) SdkMemberType(ctx android.ModuleContext, child android.ModuleProxy) android.SdkMemberType {
 // Do not add additional dependencies to the sdk.
 if b.fromAdditionalDependency {
  return nil
 }

 // If the module is a java_sdk_library then treat it as if it was specific in the java_sdk_libs
 // property, otherwise treat if it was specified in the java_header_libs property.
 if javaSdkLibrarySdkMemberType.IsInstance(ctx, child) {
  return javaSdkLibrarySdkMemberType
 }

 return javaHeaderLibsSdkMemberType
}

func (b hiddenAPIStubsDependencyTag) ExportMember() bool {
 // Export the module added via this dependency tag from the sdk.
 return true
}

// Avoid having to make stubs content explicitly visible to dependent modules.
//
// This is a temporary workaround to make it easier to migrate to bootclasspath_fragment modules
// with proper dependencies.
// TODO(b/177892522): Remove this and add needed visibility.
func (b hiddenAPIStubsDependencyTag) ExcludeFromVisibilityEnforcement() {
}

var _ android.ExcludeFromVisibilityEnforcementTag = hiddenAPIStubsDependencyTag{}
var _ android.ReplaceSourceWithPrebuilt = hiddenAPIStubsDependencyTag{}
var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{}
var _ android.SdkMemberDependencyTag = hiddenAPIStubsDependencyTag{}

// hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs
// needed to produce the hidden API monolithic stub flags file.
func hiddenAPIComputeMonolithicStubLibModules(config android.Config) map[*HiddenAPIScope][]string {
 var publicStubModules []string
 var systemStubModules []string
 var testStubModules []string
 var corePlatformStubModules []string

 if config.AlwaysUsePrebuiltSdks() {
  // Build configuration mandates using prebuilt stub modules
  publicStubModules = append(publicStubModules, "sdk_public_current_android")
  systemStubModules = append(systemStubModules, "sdk_system_current_android")
  testStubModules = append(testStubModules, "sdk_test_current_android")
 } else {
  // Use stub modules built from source
  if config.ReleaseHiddenApiExportableStubs() {
   publicStubModules = append(publicStubModules, android.SdkPublic.DefaultExportableJavaLibraryName())
   systemStubModules = append(systemStubModules, android.SdkSystem.DefaultExportableJavaLibraryName())
   testStubModules = append(testStubModules, android.SdkTest.DefaultExportableJavaLibraryName())
  } else {
   publicStubModules = append(publicStubModules, android.SdkPublic.DefaultJavaLibraryName())
   systemStubModules = append(systemStubModules, android.SdkSystem.DefaultJavaLibraryName())
   testStubModules = append(testStubModules, android.SdkTest.DefaultJavaLibraryName())
  }
 }
 // We do not have prebuilts of the core platform api yet
 if config.ReleaseHiddenApiExportableStubs() {
  corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs.exportable")
 } else {
  corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
 }

 // Allow products to define their own stubs for custom product jars that apps can use.
 publicStubModules = append(publicStubModules, config.ProductHiddenAPIStubs()...)
 systemStubModules = append(systemStubModules, config.ProductHiddenAPIStubsSystem()...)
 testStubModules = append(testStubModules, config.ProductHiddenAPIStubsTest()...)
 if config.IsEnvTrue("EMMA_INSTRUMENT") {
  // Add jacoco-stubs to public, system and test. It doesn't make any real difference as public
  // allows everyone access but it is needed to ensure consistent flags between the
  // bootclasspath fragment generated flags and the platform_bootclasspath generated flags.
  publicStubModules = append(publicStubModules, "jacoco-stubs")
  systemStubModules = append(systemStubModules, "jacoco-stubs")
  testStubModules = append(testStubModules, "jacoco-stubs")
 }

 m := map[*HiddenAPIScope][]string{}
 m[PublicHiddenAPIScope] = publicStubModules
 m[SystemHiddenAPIScope] = systemStubModules
 m[TestHiddenAPIScope] = testStubModules
 m[CorePlatformHiddenAPIScope] = corePlatformStubModules
 return m
}

// hiddenAPIAddStubLibDependencies adds dependencies onto the modules specified in
// apiScopeToStubLibModules. It adds them in a well known order and uses a HiddenAPIScope specific
// tag to identify the source of the dependency.
func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, apiScopeToStubLibModules map[*HiddenAPIScope][]string) {
 module := ctx.Module()
 for _, apiScope := range hiddenAPIScopes {
  modules := apiScopeToStubLibModules[apiScope]
  ctx.AddDependency(module, hiddenAPIStubsDependencyTag{apiScope: apiScope}, modules...)
 }
}

// hiddenAPIRetrieveDexJarBuildPath retrieves the DexJarBuildPath from the specified module, if
// available, or reports an error.
func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.ModuleProxy, kind android.SdkKind) android.Path {
 var dexJar OptionalDexJarPath
 if sdkLibrary, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
  if ctx.Config().ReleaseHiddenApiExportableStubs() {
   dexJar = sdkLibrary.ExportableStubDexJarPaths[kind]
  } else {
   dexJar = sdkLibrary.EverythingStubDexJarPaths[kind]
  }
 } else if j, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok && j.UsesLibraryDependencyInfo != nil {
  dexJar = j.DexJarBuildPath
 } else {
  ctx.ModuleErrorf("dependency %s of module type %s does not support providing a dex jar", module, ctx.OtherModuleType(module))
  return nil
 }

 if !dexJar.Valid() {
  ctx.ModuleErrorf("dependency %s does not provide a dex jar: %s", module, dexJar.InvalidReason())
  return nil
 }
 return dexJar.Path()
}

// HIDDENAPI_STUB_FLAGS_IMPL_FLAGS is the set of flags that identify implementation only signatures,
// i.e. those signatures that are not part of any API (including the hidden API).
var HIDDENAPI_STUB_FLAGS_IMPL_FLAGS = []string{}

var HIDDENAPI_FLAGS_CSV_IMPL_FLAGS = []string{"blocked"}

// buildRuleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file.
//
// The rule is initialized but not built so that the caller can modify it and select an appropriate
// name.
func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput, stubFlagSubsets SignatureCsvSubsets) {
 // Singleton rule which applies hiddenapi on all boot class path dex files.
 rule := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()

 tempPath := tempPathForRestat(ctx, outputPath)

 // Find the widest API stubs provided by the fragments on which this depends, if any.
 dependencyStubDexJars := input.DependencyStubDexJarsByScope.StubDexJarsForWidestAPIScope()

 // Add widest API stubs from the additional dependencies of this, if any.
 dependencyStubDexJars = append(dependencyStubDexJars, input.AdditionalStubDexJarsByScope.StubDexJarsForWidestAPIScope()...)

 command := rule.Command().
  Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")).
  Text("list").
  FlagForEachInput("--dependency-stub-dex=", dependencyStubDexJars).
  FlagForEachInput("--boot-dex=", bootDexJars)

 // If no module stub flags paths are provided then this must be being called for a
 // bootclasspath_fragment and not the whole platform_bootclasspath.
 if stubFlagSubsets == nil {
  // This is being run on a fragment of the bootclasspath.
  command.Flag("--fragment")
 }

 // Iterate over the api scopes in a fixed order.
 for _, apiScope := range hiddenAPIFlagScopes {
  // Merge in the stub dex jar paths for this api scope from the fragments on which it depends.
  // They will be needed to resolve dependencies from this fragment's stubs to classes in the
  // other fragment's APIs.
  var paths android.Paths
  paths = append(paths, input.DependencyStubDexJarsByScope.StubDexJarsForScope(apiScope)...)
  paths = append(paths, input.AdditionalStubDexJarsByScope.StubDexJarsForScope(apiScope)...)
  paths = append(paths, input.StubDexJarsByScope.StubDexJarsForScope(apiScope)...)
  if len(paths) > 0 {
   option := apiScope.hiddenAPIListOption
   command.FlagWithInputList(option+"=", paths, ":")
  }
 }

 // Add the output path.
 command.FlagWithOutput("--out-api-flags=", tempPath)

 // If there are stub flag files that have been generated by fragments on which this depends then
 // use them to validate the stub flag file generated by the rules created by this method.
 if !ctx.Config().DisableVerifyOverlaps() && len(stubFlagSubsets) > 0 {
  validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, stubFlagSubsets,
   HIDDENAPI_STUB_FLAGS_IMPL_FLAGS)

  // Add the file that indicates that the file generated by this is valid.
  //
  // This will cause the validation rule above to be run any time that the output of this rule
  // changes but the validation will run in parallel with other rules that depend on this file.
  command.Validation(validFile)
 }

 commitChangeForRestat(rule, tempPath, outputPath)

 rule.Build(name, desc)
}

// HiddenAPIFlagFileProperties contains paths to the flag files that can be used to augment the
// information obtained from annotations within the source code in order to create the complete set
// of flags that should be applied to the dex implementation jars on the bootclasspath.
//
// Each property contains a list of paths. With the exception of the Unsupported_packages the paths
// of each property reference a plain text file that contains a java signature per line. The flags
// for each of those signatures will be updated in a property specific way.
//
// The Unsupported_packages property contains a list of paths, each of which is a plain text file
// with one Java package per line. All members of all classes within that package (but not nested
// packages) will be updated in a property specific way.
type HiddenAPIFlagFileProperties struct {
 Hidden_api struct {
  // Marks each signature in the referenced files as being unsupported.
  Unsupported []string `android:"path"`

  // Marks each signature in the referenced files as being unsupported because it has been
  // removed. Any conflicts with other flags are ignored.
  Removed []string `android:"path"`

  // Marks each signature in the referenced files as being supported only for
  // targetSdkVersion <= R and low priority.
  Max_target_r_low_priority []string `android:"path"`

  // Marks each signature in the referenced files as being supported only for
  // targetSdkVersion <= Q.
  Max_target_q []string `android:"path"`

  // Marks each signature in the referenced files as being supported only for
  // targetSdkVersion <= P.
  Max_target_p []string `android:"path"`

  // Marks each signature in the referenced files as being supported only for
  // targetSdkVersion <= O
  // and low priority. Any conflicts with other flags are ignored.
  Max_target_o_low_priority []string `android:"path"`

  // Marks each signature in the referenced files as being blocked.
  Blocked []string `android:"path"`

  // Marks each signature in every package in the referenced files as being unsupported.
  Unsupported_packages []string `android:"path"`
 }
}

type hiddenAPIFlagFileCategory int

const (
 // The flag file category for removed members of the API.
 //
 // This is extracted from HiddenAPIFlagFileCategories as it is needed to add the dex signatures
 // list of removed API members that are generated automatically from the removed.txt files provided
 // by API stubs.
 hiddenAPIFlagFileCategoryRemoved hiddenAPIFlagFileCategory = iota
 hiddenAPIFlagFileCategoryUnsupported
 hiddenAPIFlagFileCategoryMaxTargetRLowPriority
 hiddenAPIFlagFileCategoryMaxTargetQ
 hiddenAPIFlagFileCategoryMaxTargetP
 hiddenAPIFlagFileCategoryMaxTargetOLowPriority
 hiddenAPIFlagFileCategoryBlocked
 hiddenAPIFlagFileCategoryUnsupportedPackages
)

func (c hiddenAPIFlagFileCategory) PropertyName() string {
 switch c {
 case hiddenAPIFlagFileCategoryRemoved:
  return "removed"
 case hiddenAPIFlagFileCategoryUnsupported:
  return "unsupported"
 case hiddenAPIFlagFileCategoryMaxTargetRLowPriority:
  return "max_target_r_low_priority"
 case hiddenAPIFlagFileCategoryMaxTargetQ:
  return "max_target_q"
 case hiddenAPIFlagFileCategoryMaxTargetP:
  return "max_target_p"
 case hiddenAPIFlagFileCategoryMaxTargetOLowPriority:
  return "max_target_o_low_priority"
 case hiddenAPIFlagFileCategoryBlocked:
  return "blocked"
 case hiddenAPIFlagFileCategoryUnsupportedPackages:
  return "unsupported_packages"
 default:
  panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c))
 }
}

// propertyValueReader retrieves the value of the property for this category from the set of properties.
func (c hiddenAPIFlagFileCategory) propertyValueReader(properties *HiddenAPIFlagFileProperties) []string {
 switch c {
 case hiddenAPIFlagFileCategoryRemoved:
  return properties.Hidden_api.Removed
 case hiddenAPIFlagFileCategoryUnsupported:
  return properties.Hidden_api.Unsupported
 case hiddenAPIFlagFileCategoryMaxTargetRLowPriority:
  return properties.Hidden_api.Max_target_r_low_priority
 case hiddenAPIFlagFileCategoryMaxTargetQ:
  return properties.Hidden_api.Max_target_q
 case hiddenAPIFlagFileCategoryMaxTargetP:
  return properties.Hidden_api.Max_target_p
 case hiddenAPIFlagFileCategoryMaxTargetOLowPriority:
  return properties.Hidden_api.Max_target_o_low_priority
 case hiddenAPIFlagFileCategoryBlocked:
  return properties.Hidden_api.Blocked
 case hiddenAPIFlagFileCategoryUnsupportedPackages:
  return properties.Hidden_api.Unsupported_packages
 default:
  panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c))
 }
}

// commandMutator adds the appropriate command line options for this category to the supplied command
func (c hiddenAPIFlagFileCategory) commandMutator(command *android.RuleBuilderCommand, path android.Path) {
 switch c {
 case hiddenAPIFlagFileCategoryRemoved:
  command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed")
 case hiddenAPIFlagFileCategoryUnsupported:
  command.FlagWithInput("--unsupported ", path)
 case hiddenAPIFlagFileCategoryMaxTargetRLowPriority:
  command.FlagWithInput("--max-target-r ", path).FlagWithArg("--tag ", "lo-prio")
 case hiddenAPIFlagFileCategoryMaxTargetQ:
  command.FlagWithInput("--max-target-q ", path)
 case hiddenAPIFlagFileCategoryMaxTargetP:
  command.FlagWithInput("--max-target-p ", path)
 case hiddenAPIFlagFileCategoryMaxTargetOLowPriority:
  command.FlagWithInput("--max-target-o ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio")
 case hiddenAPIFlagFileCategoryBlocked:
  command.FlagWithInput("--blocked ", path)
 case hiddenAPIFlagFileCategoryUnsupportedPackages:
  command.FlagWithInput("--unsupported ", path).Flag("--packages ")
 default:
  panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c))
 }
}

type hiddenAPIFlagFileCategories []hiddenAPIFlagFileCategory

var HiddenAPIFlagFileCategories = hiddenAPIFlagFileCategories{
 // See HiddenAPIFlagFileProperties.Unsupported
 hiddenAPIFlagFileCategoryUnsupported,
 // See HiddenAPIFlagFileProperties.Removed
 hiddenAPIFlagFileCategoryRemoved,
 // See HiddenAPIFlagFileProperties.Max_target_r_low_priority
 hiddenAPIFlagFileCategoryMaxTargetRLowPriority,
 // See HiddenAPIFlagFileProperties.Max_target_q
 hiddenAPIFlagFileCategoryMaxTargetQ,
 // See HiddenAPIFlagFileProperties.Max_target_p
 hiddenAPIFlagFileCategoryMaxTargetP,
 // See HiddenAPIFlagFileProperties.Max_target_o_low_priority
 hiddenAPIFlagFileCategoryMaxTargetOLowPriority,
 // See HiddenAPIFlagFileProperties.Blocked
 hiddenAPIFlagFileCategoryBlocked,
 // See HiddenAPIFlagFileProperties.Unsupported_packages
 hiddenAPIFlagFileCategoryUnsupportedPackages,
}

// FlagFilesByCategory maps a hiddenAPIFlagFileCategory to the paths to the files in that category.
// @auto-generate: gob
type FlagFilesByCategory map[hiddenAPIFlagFileCategory]android.Paths

// append the supplied flags files to the corresponding category in this map.
func (s FlagFilesByCategory) append(other FlagFilesByCategory) {
 for _, category := range HiddenAPIFlagFileCategories {
  s[category] = append(s[category], other[category]...)
 }
}

// sort the paths for each category in this map.
func (s FlagFilesByCategory) sort() {
 for category, value := range s {
  s[category] = android.SortedUniquePaths(value)
 }
}

// HiddenAPIInfo contains information provided by the hidden API processing.
//
// That includes paths resolved from HiddenAPIFlagFileProperties and also generated by hidden API
// processing.
// @auto-generate: gob
type HiddenAPIInfo struct {
 // FlagFilesByCategory maps from the flag file category to the paths containing information for
 // that category.
 FlagFilesByCategory FlagFilesByCategory

 // The paths to the stub dex jars for each of the *HiddenAPIScope in hiddenAPIScopes provided by
 // this fragment and the fragments on which this depends.
 TransitiveStubDexJarsByScope StubDexJarsByModule

 // The output from the hidden API processing needs to be made available to other modules.
 HiddenAPIFlagOutput
}

func newHiddenAPIInfo() *HiddenAPIInfo {
 info := HiddenAPIInfo{
  FlagFilesByCategory:          FlagFilesByCategory{},
  TransitiveStubDexJarsByScope: StubDexJarsByModule{},
 }
 return &info
}

func (i *HiddenAPIInfo) mergeFromFragmentDeps(ctx android.ModuleContext, fragments []android.ModuleProxy) {
 // Merge all the information from the fragments. The fragments form a DAG so it is possible that
 // this will introduce duplicates so they will be resolved after processing all the fragments.
 for _, fragment := range fragments {
  if info, ok := android.OtherModuleProvider(ctx, fragment, HiddenAPIInfoProvider); ok {
   i.TransitiveStubDexJarsByScope.addStubDexJarsByModule(info.TransitiveStubDexJarsByScope)
  }
 }
}

// StubFlagSubset returns a SignatureCsvSubset that contains a path to a filtered-stub-flags.csv
// file and a path to a signature-patterns.csv file that defines a subset of the monolithic stub
// flags file, i.e. out/soong/hiddenapi/hiddenapi-stub-flags.txt, against which it will be compared.
func (i *HiddenAPIInfo) StubFlagSubset() SignatureCsvSubset {
 return SignatureCsvSubset{i.FilteredStubFlagsPath, i.SignaturePatternsPath}
}

// FlagSubset returns a SignatureCsvSubset that contains a path to a filtered-flags.csv file and a
// path to a signature-patterns.csv file that defines a subset of the monolithic flags file, i.e.
// out/soong/hiddenapi/hiddenapi-flags.csv, against which it will be compared.
func (i *HiddenAPIInfo) FlagSubset() SignatureCsvSubset {
 return SignatureCsvSubset{i.FilteredFlagsPath, i.SignaturePatternsPath}
}

var HiddenAPIInfoProvider = blueprint.NewProvider[HiddenAPIInfo]()

// HiddenAPIInfoForSdk contains information provided by the hidden API processing for use
// by the sdk snapshot.
//
// That includes paths resolved from HiddenAPIFlagFileProperties and also generated by hidden API
// processing.
// @auto-generate: gob
type HiddenAPIInfoForSdk struct {
 // FlagFilesByCategory maps from the flag file category to the paths containing information for
 // that category.
 FlagFilesByCategory FlagFilesByCategory

 // The output from the hidden API processing needs to be made available to other modules.
 HiddenAPIFlagOutput
}

// Provides hidden API info for the sdk snapshot.
var HiddenAPIInfoForSdkProvider = blueprint.NewProvider[HiddenAPIInfoForSdk]()

// ModuleStubDexJars contains the stub dex jars provided by a single module.
//
// It maps a HiddenAPIScope name to the path to stub dex jars appropriate for that scope. See
// hiddenAPIScopes for a list of the acceptable *HiddenAPIScope values.
// @auto-generate: gob
type ModuleStubDexJars map[string]android.Path

// stubDexJarForWidestAPIScope returns the stub dex jars for the widest API scope provided by this
// map.
//
// The relative width of APIs is determined by their order in hiddenAPIScopes.
func (s ModuleStubDexJars) stubDexJarForWidestAPIScope() android.Path {
 for i := len(hiddenAPIScopes) - 1; i >= 0; i-- {
  apiScope := hiddenAPIScopes[i]
  if stubsForAPIScope, ok := s[apiScope.name]; ok {
   return stubsForAPIScope
  }
 }

 return nil
}

// StubDexJarsByModule contains the stub dex jars provided by a set of modules.
//
// It maps a module name to the path to the stub dex jars provided by that module.
type StubDexJarsByModule map[string]ModuleStubDexJars

// addStubDexJar adds a stub dex jar path provided by the specified module for the specified scope.
func (s StubDexJarsByModule) addStubDexJar(ctx android.ModuleContext, module android.ModuleProxy, scope *HiddenAPIScope, stubDexJar android.Path) {
 name := android.RemoveOptionalPrebuiltPrefix(module.Name())

 // Each named module provides one dex jar for each scope. However, in some cases different API
 // versions of a single classes are provided by separate modules. e.g. the core platform
 // version of java.lang.Object is provided by the legacy.art.module.platform.api module but the
 // public version is provided by the art.module.public.api module. In those cases it is necessary
 // to treat all those modules as they were the same name, otherwise it will result in multiple
 // definitions of a single class being passed to hidden API processing which will cause an error.
 if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule || name == scope.nonUpdatableFromTextModule {
  // Treat all *android-non-updatable* modules as if they were part of an android-non-updatable
  // java_sdk_library.
  // TODO(b/192067200): Remove once android-non-updatable is a java_sdk_library or equivalent.
  name = "android-non-updatable"
 } else if name == "legacy.art.module.platform.api" {
  // Treat legacy.art.module.platform.api as if it was an API scope provided by the
  // art.module.public.api java_sdk_library which will be the case once the former has been
  // migrated to a module_lib API.
  name = "art.module.public.api"
 } else if name == "legacy.i18n.module.platform.api" {
  // Treat legacy.i18n.module.platform.api as if it was an API scope provided by the
  // i18n.module.public.api java_sdk_library which will be the case once the former has been
  // migrated to a module_lib API.
  name = "i18n.module.public.api"
 } else if name == "conscrypt.module.platform.api" {
  // Treat conscrypt.module.platform.api as if it was an API scope provided by the
  // conscrypt.module.public.api java_sdk_library which will be the case once the former has been
  // migrated to a module_lib API.
  name = "conscrypt.module.public.api"
 } else {
  if slcDepInfo, ok := android.OtherModuleProvider(ctx, module, SdkLibraryComponentDependencyInfoProvider); ok {
   if sdkLibraryName := slcDepInfo.SdkLibraryName; sdkLibraryName != nil {
    // The module is a component of a java_sdk_library so use the name of the java_sdk_library.
    // e.g. if this module is `foo.system.stubs` and is part of the `foo` java_sdk_library then
    // use `foo` as the name.
    name = *sdkLibraryName
   }
  }
 }
 stubDexJarsByScope := s[name]
 if stubDexJarsByScope == nil {
  stubDexJarsByScope = ModuleStubDexJars{}
  s[name] = stubDexJarsByScope
 }
 stubDexJarsByScope[scope.name] = stubDexJar
}

// addStubDexJarsByModule adds the stub dex jars in the supplied StubDexJarsByModule to this map.
func (s StubDexJarsByModule) addStubDexJarsByModule(other StubDexJarsByModule) {
 for module, stubDexJarsByScope := range other {
  s[module] = stubDexJarsByScope
 }
}

// StubDexJarsForWidestAPIScope returns a list of stub dex jars containing the widest API scope
// provided by each module.
//
// The relative width of APIs is determined by their order in hiddenAPIScopes.
func (s StubDexJarsByModule) StubDexJarsForWidestAPIScope() android.Paths {
 stubDexJars := android.Paths{}
 modules := android.SortedKeys(s)
 for _, module := range modules {
  stubDexJarsByScope := s[module]

  stubDexJars = append(stubDexJars, stubDexJarsByScope.stubDexJarForWidestAPIScope())
 }

 return stubDexJars
}

// StubDexJarsForScope returns a list of stub dex jars containing the stub dex jars provided by each
// module for the specified scope.
//
// If a module does not provide a stub dex jar for the supplied scope then it does not contribute to
// the returned list.
func (s StubDexJarsByModule) StubDexJarsForScope(scope *HiddenAPIScope) android.Paths {
 stubDexJars := android.Paths{}
 modules := android.SortedKeys(s)
 for _, module := range modules {
  stubDexJarsByScope := s[module]
  // Not every module will have the same set of
  if jars, ok := stubDexJarsByScope[scope.name]; ok {
   stubDexJars = append(stubDexJars, jars)
  }
 }

 return stubDexJars
}

// @auto-generate: gob
type HiddenAPIPropertyInfo struct {
 // FlagFilesByCategory contains the flag files that override the initial flags that are derived
 // from the stub dex files.
 FlagFilesByCategory FlagFilesByCategory

 // See HiddenAPIFlagFileProperties.Package_prefixes
 PackagePrefixes []string

 // See HiddenAPIFlagFileProperties.Single_packages
 SinglePackages []string

 // See HiddenAPIFlagFileProperties.Split_packages
 SplitPackages []string
}

var hiddenAPIPropertyInfoProvider = blueprint.NewProvider[HiddenAPIPropertyInfo]()

// newHiddenAPIPropertyInfo creates a new initialized HiddenAPIPropertyInfo struct.
func newHiddenAPIPropertyInfo() HiddenAPIPropertyInfo {
 return HiddenAPIPropertyInfo{
  FlagFilesByCategory: FlagFilesByCategory{},
 }
}

// extractFlagFilesFromProperties extracts the paths to flag files that are specified in the
// supplied properties and stores them in this struct.
func (i *HiddenAPIPropertyInfo) extractFlagFilesFromProperties(ctx android.ModuleContext, p *HiddenAPIFlagFileProperties) {
 for _, category := range HiddenAPIFlagFileCategories {
  paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p))
  i.FlagFilesByCategory[category] = paths
 }
}

// extractPackageRulesFromProperties extracts the package rules that are specified in the supplied
// properties and stores them in this struct.
func (i *HiddenAPIPropertyInfo) extractPackageRulesFromProperties(ctx android.ModuleContext, p *HiddenAPIPackageProperties) {
 i.PackagePrefixes = p.Hidden_api.Package_prefixes.GetOrDefault(ctx, nil)
 i.SinglePackages = p.Hidden_api.Single_packages
 i.SplitPackages = p.Hidden_api.Split_packages.GetOrDefault(ctx, nil)
}

func (i *HiddenAPIPropertyInfo) gatherPropertyInfo(ctx android.ModuleContext, contents []android.ModuleProxy) {
 for _, module := range contents {
  if info, ok := android.OtherModuleProvider(ctx, module, hiddenAPIPropertyInfoProvider); ok {
   i.FlagFilesByCategory.append(info.FlagFilesByCategory)
   i.PackagePrefixes = append(i.PackagePrefixes, info.PackagePrefixes...)
   i.SinglePackages = append(i.SinglePackages, info.SinglePackages...)
   i.SplitPackages = append(i.SplitPackages, info.SplitPackages...)
  }
 }

 // Dedup and sort the information to ensure consistent builds.
 i.FlagFilesByCategory.sort()
 i.PackagePrefixes = android.SortedUniqueStrings(i.PackagePrefixes)
 i.SinglePackages = android.SortedUniqueStrings(i.SinglePackages)
 i.SplitPackages = android.SortedUniqueStrings(i.SplitPackages)
}

// HiddenAPIFlagInput encapsulates information obtained from a module and its dependencies that are
// needed for hidden API flag generation.
type HiddenAPIFlagInput struct {
 HiddenAPIPropertyInfo

 // StubDexJarsByScope contains the stub dex jars for different *HiddenAPIScope and which determine
 // the initial flags for each dex member.
 StubDexJarsByScope StubDexJarsByModule

 // DependencyStubDexJarsByScope contains the stub dex jars provided by the fragments on which this
 // depends. It is the result of merging HiddenAPIInfo.TransitiveStubDexJarsByScope from each
 // fragment on which this depends.
 DependencyStubDexJarsByScope StubDexJarsByModule

 // AdditionalStubDexJarsByScope contains stub dex jars provided by other modules in addition to
 // the ones that are obtained from fragments on which this depends.
 //
 // These are kept separate from stub dex jars in HiddenAPIFlagInput.DependencyStubDexJarsByScope
 // as there are not propagated transitively to other fragments that depend on this.
 AdditionalStubDexJarsByScope StubDexJarsByModule

 // RemovedTxtFiles is the list of removed.txt files provided by java_sdk_library modules that are
 // specified in the bootclasspath_fragment's stub_libs and contents properties.
 RemovedTxtFiles android.Paths
}

// newHiddenAPIFlagInput creates a new initialized HiddenAPIFlagInput struct.
func newHiddenAPIFlagInput() HiddenAPIFlagInput {
 input := HiddenAPIFlagInput{
  HiddenAPIPropertyInfo:        newHiddenAPIPropertyInfo(),
  StubDexJarsByScope:           StubDexJarsByModule{},
  DependencyStubDexJarsByScope: StubDexJarsByModule{},
  AdditionalStubDexJarsByScope: StubDexJarsByModule{},
 }

 return input
}

// gatherStubLibInfo gathers information from the stub libs needed by hidden API processing from the
// dependencies added in hiddenAPIAddStubLibDependencies.
//
// That includes paths to the stub dex jars as well as paths to the *removed.txt files.
func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, contents []android.ModuleProxy) {
 addFromModule := func(ctx android.ModuleContext, module android.ModuleProxy, apiScope *HiddenAPIScope) {
  sdkKind := apiScope.sdkKind
  dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, sdkKind)
  if dexJar != nil {
   i.StubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar)
  }

  if sdkLibrary, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
   removedTxtFile := sdkLibrary.RemovedTxtFiles[sdkKind]
   i.RemovedTxtFiles = append(i.RemovedTxtFiles, removedTxtFile.AsPaths()...)
  }
 }

 // If the contents includes any java_sdk_library modules then add them to the stubs.
 for _, module := range contents {
  if _, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
   // Add information for every possible API scope needed by hidden API.
   for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
    addFromModule(ctx, module, apiScope)
   }
  }
 }

 ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
  tag := ctx.OtherModuleDependencyTag(module)
  if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok {
   apiScope := hiddenAPIStubsTag.apiScope
   if hiddenAPIStubsTag.fromAdditionalDependency {
    dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, apiScope.sdkKind)
    if dexJar != nil {
     i.AdditionalStubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar)
    }
   } else {
    addFromModule(ctx, module, apiScope)
   }
  }
 })

 // Normalize the paths, i.e. remove duplicates and sort.
 i.RemovedTxtFiles = android.SortedUniquePaths(i.RemovedTxtFiles)
}

func (i *HiddenAPIFlagInput) transitiveStubDexJarsByScope() StubDexJarsByModule {
 transitive := i.DependencyStubDexJarsByScope
 transitive.addStubDexJarsByModule(i.StubDexJarsByScope)
 return transitive
}

// HiddenAPIFlagOutput contains paths to output files from the hidden API flag generation for a
// bootclasspath_fragment module.
// @auto-generate: gob
type HiddenAPIFlagOutput struct {
 // The path to the generated annotation-flags.csv file.
 AnnotationFlagsPath android.Path

 // The path to the generated metadata.csv file.
 MetadataPath android.Path

 // The path to the generated index.csv file.
 IndexPath android.Path

 // The path to the generated stub-flags.csv file.
 StubFlagsPath android.Path

 // The path to the generated all-flags.csv file.
 AllFlagsPath android.Path

 // The path to the generated signature-patterns.txt file which defines the subset of the
 // monolithic hidden API files provided in this.
 SignaturePatternsPath android.Path

 // The path to the generated filtered-stub-flags.csv file.
 FilteredStubFlagsPath android.Path

 // The path to the generated filtered-flags.csv file.
 FilteredFlagsPath android.Path
}

// bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex
// path.
type bootDexJarByModule map[string]android.Path

// addPath adds the path for a module to the map.
func (b bootDexJarByModule) addPath(module android.ModuleProxy, path android.Path) {
 b[android.RemoveOptionalPrebuiltPrefix(module.Name())] = path
}

// bootDexJars returns the boot dex jar paths sorted by their keys.
func (b bootDexJarByModule) bootDexJars() android.Paths {
 paths := android.Paths{}
 for _, k := range android.SortedKeys(b) {
  paths = append(paths, b[k])
 }
 return paths
}

// bootDexJarsWithoutCoverage returns the boot dex jar paths sorted by their keys without coverage
// libraries if present.
func (b bootDexJarByModule) bootDexJarsWithoutCoverage() android.Paths {
 paths := android.Paths{}
 for _, k := range android.SortedKeys(b) {
  if k == "jacocoagent" {
   continue
  }
  paths = append(paths, b[k])
 }
 return paths
}

// HiddenAPIOutput encapsulates the output from the hidden API processing.
type HiddenAPIOutput struct {
 HiddenAPIFlagOutput

 // The map from base module name to the path to the encoded boot dex file.
 // This field is not available in prebuilt apexes
 EncodedBootDexFilesByModule bootDexJarByModule
}

// pathForValidation creates a path of the same type as the supplied type but with a name of
// <path>.valid.
//
// e.g. If path is an OutputPath for out/soong/hiddenapi/hiddenapi-flags.csv then this will return
// an OutputPath for out/soong/hiddenapi/hiddenapi-flags.csv.valid
func pathForValidation(ctx android.PathContext, path android.WritablePath) android.WritablePath {
 extWithoutLeadingDot := strings.TrimPrefix(path.Ext(), ".")
 return path.ReplaceExtension(ctx, extWithoutLeadingDot+".valid")
}

// buildRuleToGenerateHiddenApiFlags creates a rule to create the monolithic hidden API flags from
// the flags from all the modules, the stub flags, augmented with some additional configuration
// files.
//
// baseFlagsPath is the path to the flags file containing all the information from the stubs plus
// an entry for every single member in the dex implementation jars of the individual modules. Every
// signature in any of the other files MUST be included in this file.
//
// annotationFlags is the path to the annotation flags file generated from annotation information
// in each module.
//
// hiddenAPIInfo is a struct containing paths to files that augment the information provided by
// the annotationFlags.
func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string,
 outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlagPaths android.Paths,
 flagFilesByCategory FlagFilesByCategory, flagSubsets SignatureCsvSubsets, generatedRemovedDexSignatures android.OptionalPath) {

 // Create the rule that will generate the flag files.
 tempPath := tempPathForRestat(ctx, outputPath)
 rule := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()
 command := rule.Command().
  BuiltTool("generate_hiddenapi_lists").
  FlagWithInput("--csv ", baseFlagsPath).
  Inputs(annotationFlagPaths).
  FlagWithOutput("--output ", tempPath)

 // Add the options for the different categories of flag files.
 for _, category := range HiddenAPIFlagFileCategories {
  paths := flagFilesByCategory[category]
  for _, path := range paths {
   category.commandMutator(command, path)
  }
 }

 // If available then pass the automatically generated file containing dex signatures of removed
 // API members to the rule so they can be marked as removed.
 if generatedRemovedDexSignatures.Valid() {
  hiddenAPIFlagFileCategoryRemoved.commandMutator(command, generatedRemovedDexSignatures.Path())
 }

 commitChangeForRestat(rule, tempPath, outputPath)

 // If there are flag files that have been generated by fragments on which this depends then use
 // them to validate the flag file generated by the rules created by this method.
 if !ctx.Config().DisableVerifyOverlaps() && len(flagSubsets) > 0 {
  validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, flagSubsets,
   HIDDENAPI_FLAGS_CSV_IMPL_FLAGS)

  // Add the file that indicates that the file generated by this is valid.
  //
  // This will cause the validation rule above to be run any time that the output of this rule
  // changes but the validation will run in parallel with other rules that depend on this file.
  command.Validation(validFile)
 }

 rule.Build(name, desc)
}

// SignatureCsvSubset describes a subset of a monolithic flags file, i.e. either
// out/soong/hiddenapi/hiddenapi-stub-flags.txt or out/soong/hiddenapi/hiddenapi-flags.csv
// @auto-generate: gob
type SignatureCsvSubset struct {
 // The path to the CSV file containing hidden API flags.
 //
 // It has the dex member signature as the first column, with flags, one per column, in the
 // subsequent columns.
 CsvFile android.Path

 // The path to the CSV file containing the signature patterns.
 //
 // It is a single column CSV file with the column containing a signature pattern.
 SignaturePatternsFile android.Path
}

// @auto-generate: gob
type SignatureCsvSubsets []SignatureCsvSubset

func (s SignatureCsvSubsets) RelativeToTop() []string {
 result := []string{}
 for _, subset := range s {
  result = append(result, fmt.Sprintf("%s:%s", subset.CsvFile.RelativeToTop(), subset.SignaturePatternsFile.RelativeToTop()))
 }
 return result
}

// buildRuleSignaturePatternsFile creates a rule to generate a file containing the set of signature
// patterns that will select a subset of the monolithic flags.
func buildRuleSignaturePatternsFile(
 ctx android.ModuleContext, flagsPath android.Path,
 splitPackages []string, packagePrefixes []string, singlePackages []string,
 suffix string) android.Path {
 hiddenApiSubDir := "modular-hiddenapi" + suffix

 patternsFile := android.PathForModuleOut(ctx, hiddenApiSubDir, "signature-patterns.csv")
 // Create a rule to validate the output from the following rule.
 rule := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()

 // Quote any * in the packages to avoid them being expanded by the shell.
 quotedSplitPackages := []string{}
 for _, pkg := range splitPackages {
  quotedSplitPackages = append(quotedSplitPackages, strings.ReplaceAll(pkg, "*", "\\*"))
 }

 rule.Command().
  BuiltTool("signature_patterns").
  FlagWithInput("--flags ", flagsPath).
  FlagForEachArg("--split-package ", quotedSplitPackages).
  FlagForEachArg("--package-prefix ", packagePrefixes).
  FlagForEachArg("--single-package ", singlePackages).
  FlagWithOutput("--output ", patternsFile)
 rule.Build("hiddenAPISignaturePatterns"+suffix, "hidden API signature patterns"+suffix)

 return patternsFile
}

// buildRuleRemoveSignaturesWithImplementationFlags creates a rule that will remove signatures from
// the input flags file which have only the implementation flags, i.e. are not part of an API.
//
// The implementationFlags specifies the set of default flags that identifies the signature of a
// private, implementation only, member. Signatures that match those flags are removed from the
// flags as they are implementation only.
//
// This is used to remove implementation only signatures from the signature files that are persisted
// in the sdk snapshot as the sdk snapshots should not include implementation details. The
// signatures generated by this method will be compared by the buildRuleValidateOverlappingCsvFiles
// method which treats any missing signatures as if they were implementation only signatures.
func buildRuleRemoveSignaturesWithImplementationFlags(ctx android.BuilderContext,
 name string, desc string, inputPath android.Path, filteredPath android.WritablePath,
 implementationFlags []string) {

 rule := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()
 implementationFlagPattern := ""
 for _, implementationFlag := range implementationFlags {
  implementationFlagPattern = implementationFlagPattern + "," + implementationFlag
 }
 rule.Command().
  Text(`grep -vE "^[^,]+` + implementationFlagPattern + `$"`).Input(inputPath).
  Text(">").Output(filteredPath).
  // Grep's exit code depends on whether it finds anything. It is 0 (build success) when it finds
  // something and 1 (build failure) when it does not and 2 (when it encounters an error).
  // However, while it is unlikely it is not an error if this does not find any matches. The
  // following will only run if the grep does not find something and in that case it will treat
  // an exit code of 1 as success and anything else as failure.
  Text("|| test $? -eq 1")
 rule.Build(name, desc)
}

// buildRuleValidateOverlappingCsvFiles checks that the modular CSV files, i.e. the files generated
// by the individual bootclasspath_fragment modules are subsets of the monolithic CSV file.
//
// The implementationFlags specifies the set of default flags that identifies the signature of a
// private, implementation only, member. A signature which is present in a monolithic flags subset
// defined by SignatureCsvSubset but which is not present in the flags file from the corresponding
// module is assumed to be an implementation only member and so must have these flags.
func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string,
 monolithicFilePath android.WritablePath, csvSubsets SignatureCsvSubsets,
 implementationFlags []string) android.WritablePath {
 // The file which is used to record that the flags file is valid.
 validFile := pathForValidation(ctx, monolithicFilePath)

 // Create a rule to validate the output from the following rule.
 rule := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()
 command := rule.Command().
  BuiltTool("verify_overlaps").
  FlagWithInput("--monolithic-flags ", monolithicFilePath)

 for _, subset := range csvSubsets {
  command.
   Flag("--module-flags ").
   Textf("%s:%s", subset.CsvFile, subset.SignaturePatternsFile).
   Implicit(subset.CsvFile).Implicit(subset.SignaturePatternsFile)
 }

 for _, implementationFlag := range implementationFlags {
  command.FlagWithArg("--implementation-flag ", implementationFlag)
 }

 // If validation passes then update the file that records that.
 command.Text("&& touch").Output(validFile)
 rule.Build(name+"Validation", desc+" validation")

 return validFile
}

// hiddenAPIFlagRulesForBootclasspathFragment will generate all the flags for a fragment of the
// bootclasspath.
//
// It takes:
// * Map from android.SdkKind to stub dex jar paths defining the API for that sdk kind.
// * The list of modules that are the contents of the fragment.
// * The additional manually curated flag files to use.
//
// It generates:
// * stub-flags.csv
// * annotation-flags.csv
// * metadata.csv
// * index.csv
// * all-flags.csv
func hiddenAPIFlagRulesForBootclasspathFragment(ctx android.ModuleContext, bootDexInfoByModule bootDexInfoByModule, contents []android.ModuleProxy, input HiddenAPIFlagInput, suffix string) HiddenAPIFlagOutput {
 hiddenApiSubDir := "modular-hiddenapi" + suffix

 // Generate the stub-flags.csv.
 stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv")
 buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "modularHiddenAPIStubFlagsFile"+suffix, "modular hiddenapi stub flags", stubFlagsCSV, bootDexInfoByModule.bootDexJars(), input, nil)

 // Extract the classes jars from the contents.
 classesJars := extractClassesJarsFromModules(ctx, contents)

 // Generate the set of flags from the annotations in the source code.
 annotationFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "annotation-flags.csv")
 buildRuleToGenerateAnnotationFlags(ctx, "modular hiddenapi annotation flags"+suffix, classesJars, stubFlagsCSV, annotationFlagsCSV)

 // Generate the metadata from the annotations in the source code.
 metadataCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "metadata.csv")
 buildRuleToGenerateMetadata(ctx, "modular hiddenapi metadata"+suffix, classesJars, stubFlagsCSV, metadataCSV)

 // Generate the index file from the CSV files in the classes jars.
 indexCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "index.csv")
 buildRuleToGenerateIndex(ctx, "modular hiddenapi index"+suffix, classesJars, indexCSV)

 // Removed APIs need to be marked and in order to do that the hiddenAPIInfo needs to specify files
 // containing dex signatures of all the removed APIs. In the monolithic files that is done by
 // manually combining all the removed.txt files for each API and then converting them to dex
 // signatures, see the combined-removed-dex module. This does that automatically by using the
 // *removed.txt files retrieved from the java_sdk_library modules that are specified in the
 // stub_libs and contents properties of a bootclasspath_fragment.
 removedDexSignatures := buildRuleToGenerateRemovedDexSignatures(ctx, suffix, input.RemovedTxtFiles)

 // Generate the all-flags.csv which are the flags that will, in future, be encoded into the dex
 // files.
 allFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv")
 buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags"+suffix, "modular hiddenapi all flags"+suffix, allFlagsCSV, stubFlagsCSV, android.Paths{annotationFlagsCSV}, input.FlagFilesByCategory, nil, removedDexSignatures)

 // Generate the filtered-stub-flags.csv file which contains the filtered stub flags that will be
 // compared against the monolithic stub flags.
 filteredStubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-stub-flags.csv")
 buildRuleRemoveSignaturesWithImplementationFlags(ctx, "modularHiddenApiFilteredStubFlags"+suffix,
  "modular hiddenapi filtered stub flags"+suffix, stubFlagsCSV, filteredStubFlagsCSV,
  HIDDENAPI_STUB_FLAGS_IMPL_FLAGS)

 // Generate the filtered-flags.csv file which contains the filtered flags that will be compared
 // against the monolithic flags.
 filteredFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-flags.csv")
 buildRuleRemoveSignaturesWithImplementationFlags(ctx, "modularHiddenApiFilteredFlags"+suffix,
  "modular hiddenapi filtered flags"+suffix, allFlagsCSV, filteredFlagsCSV,
  HIDDENAPI_FLAGS_CSV_IMPL_FLAGS)

 // Store the paths in the info for use by other modules and sdk snapshot generation.
 return HiddenAPIFlagOutput{
  AnnotationFlagsPath:   annotationFlagsCSV,
  MetadataPath:          metadataCSV,
  IndexPath:             indexCSV,
  StubFlagsPath:         stubFlagsCSV,
  AllFlagsPath:          allFlagsCSV,
  FilteredStubFlagsPath: filteredStubFlagsCSV,
  FilteredFlagsPath:     filteredFlagsCSV,
 }
}

// hiddenAPIEncodeRulesForBootclasspathFragment generates rules to encode hidden API flags into the
// dex jars in bootDexInfoByModule.
func hiddenAPIEncodeRulesForBootclasspathFragment(ctx android.ModuleContext, bootDexInfoByModule bootDexInfoByModule, allFlagsCSV android.Path) bootDexJarByModule {
 // Encode the flags into the boot dex files.
 encodedBootDexJarsByModule := bootDexJarByModule{}
 outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath
 for _, name := range android.SortedKeys(bootDexInfoByModule) {
  bootDexInfo := bootDexInfoByModule[name]
  unencodedDex := bootDexInfo.path
  encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, bootDexInfo.minSdkVersion, outputDir)
  encodedBootDexJarsByModule[name] = encodedDex
 }
 return encodedBootDexJarsByModule
}

func buildRuleToGenerateRemovedDexSignatures(ctx android.ModuleContext, suffix string, removedTxtFiles android.Paths) android.OptionalPath {
 if len(removedTxtFiles) == 0 {
  return android.OptionalPath{}
 }

 output := android.PathForModuleOut(ctx, "module-hiddenapi"+suffix, "removed-dex-signatures.txt")

 rule := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()
 rule.Command().
  BuiltTool("metalava").
  Text("signature-to-dex").
  Inputs(removedTxtFiles).
  FlagWithOutput("--out ", output)
 rule.Build("modular-hiddenapi-removed-dex-signatures"+suffix, "modular hiddenapi removed dex signatures"+suffix)
 return android.OptionalPathForPath(output)
}

// extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules.
// This information can come from two mechanisms
// 1. New: Direct deps to _selected_ apexes. The apexes contain a ApexExportsInfo
// 2. Legacy: An edge to java_sdk_library(_import) module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes
// TODO: b/458374506 - Once all mainline modules have been flagged, drop (2)
func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.ModuleProxy) bootDexJarByModule {
 bootDexJars := bootDexJarByModule{}

 apexNameToApexExportsInfoMap := getApexNameToApexExportsInfoMap(ctx)
 // For ART and mainline module jars, query apexNameToApexExportsInfoMap to get the dex file
 apexJars := dexpreopt.GetGlobalConfig(ctx).ArtApexJars.AppendList(&dexpreopt.GetGlobalConfig(ctx).ApexBootJars)
 for i := 0; i < apexJars.Len(); i++ {
  if dex, found := apexNameToApexExportsInfoMap.javaLibraryDexPathOnHost(ctx, apexJars.Apex(i), apexJars.Jar(i)); found {
   bootDexJars[apexJars.Jar(i)] = dex
  }
 }

 // TODO - b/458374506: Drop the legacy mechanism
 for _, module := range contents {
  if _, exists := bootDexJars[android.RemoveOptionalPrebuiltPrefix(module.Name())]; exists {
   continue
  }
  bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, module)
  bootDexJars.addPath(module, bootDexJar)
 }
 return bootDexJars
}

// bootDexInfo encapsulates both the path and uncompressDex status retrieved from a hiddenAPIModule.
type bootDexInfo struct {
 // The path to the dex jar that has not had hidden API flags encoded into it.
 path android.Path

 // Indicates whether the dex jar needs uncompressing before encoding.
 uncompressDex bool

 // The minimum sdk version that the dex jar will be used on.
 minSdkVersion android.ApiLevel
}

// bootDexInfoByModule is a map from module name (as returned by module.Name()) to the boot dex
// path (as returned by hiddenAPIModule.bootDexJar()) and the uncompressDex flag.
type bootDexInfoByModule map[string]bootDexInfo

// bootDexJars returns the boot dex jar paths sorted by their keys.
func (b bootDexInfoByModule) bootDexJars() android.Paths {
 paths := android.Paths{}
 for _, m := range android.SortedKeys(b) {
  paths = append(paths, b[m].path)
 }
 return paths
}

// extractBootDexInfoFromModules extracts the boot dex jar and uncompress dex state from
// each of the supplied modules which must implement hiddenAPIModule.
func extractBootDexInfoFromModules(ctx android.ModuleContext, contents []android.ModuleProxy) bootDexInfoByModule {
 bootDexJarsByModule := bootDexInfoByModule{}
 for _, module := range contents {
  bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, module)
  info := android.OtherModuleProviderOrDefault(ctx, module, JavaInfoProvider)
  commonInfo := android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider)
  bootDexJarsByModule[module.Name()] = bootDexInfo{
   path:          bootDexJar,
   uncompressDex: Bool(info.UncompressDexState),
   minSdkVersion: *commonInfo.MinSdkVersion.ApiLevel,
  }
 }

 return bootDexJarsByModule
}

// retrieveBootDexJarFromHiddenAPIModule retrieves the boot dex jar from the hiddenAPIModule.
//
// If the module does not provide a boot dex jar, i.e. the returned boot dex jar is unset or
// invalid, then create a fake path and either report an error immediately or defer reporting of the
// error until the path is actually used.
func retrieveBootDexJarFromHiddenAPIModule(ctx android.ModuleContext, module android.ModuleProxy) android.Path {
 info := android.OtherModuleProviderOrDefault(ctx, module, JavaInfoProvider)
 if !info.BootDexJarPath.Valid() {
  fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/boot-dex/%s.jar", module.Name()))
  handleMissingDexBootFile(ctx, module, fake, info.BootDexJarPath.InvalidReason())
  return fake
 }

 return info.BootDexJarPath.Path()
}

// extractClassesJarsFromModules extracts the class jars from the supplied modules.
func extractClassesJarsFromModules(ctx android.ModuleContext, contents []android.ModuleProxy) android.Paths {
 classesJars := android.Paths{}
 for _, module := range contents {
  if info, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
   classesJars = append(classesJars, info.HiddenapiClassesJarPaths...)
  }
 }
 return classesJars
}

// deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by
// Soong but should instead only be reported in ninja if the file is actually built.
func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.ModuleOrProxy) bool {
 // Any missing dependency should be allowed.
 if ctx.Config().AllowMissingDependencies() {
  return true
 }

 // This is called for both platform_bootclasspath and bootclasspath_fragment modules.
 //
 // A bootclasspath_fragment module should only use the APEX variant of source or prebuilt modules.
 // Ideally, a bootclasspath_fragment module should never have a platform variant created for it
 // but unfortunately, due to b/187910671 it does.
 //
 // That causes issues when obtaining a boot dex jar for a prebuilt module as a prebuilt module
 // used by a bootclasspath_fragment can only provide a boot dex jar when it is part of APEX, i.e.
 // has an APEX variant not a platform variant.
 //
 // There are some other situations when a prebuilt module used by a bootclasspath_fragment cannot
 // provide a boot dex jar:
 // 1. If the bootclasspath_fragment is not exported by the prebuilt_apex/apex_set module then it
 //    does not have an APEX variant and only has a platform variant and neither do its content
 //    modules.
 // 2. Some build configurations, e.g. setting TARGET_BUILD_USE_PREBUILT_SDKS causes all
 //    java_sdk_library_import modules to be treated as preferred and as many of them are not part
 //    of an apex they cannot provide a boot dex jar.
 //
 // The first case causes problems when the affected prebuilt modules are preferred but that is an
 // invalid configuration and it is ok for it to fail as the work to enable that is not yet
 // complete. The second case is used for building targets that do not use boot dex jars and so
 // deferring error reporting to ninja is fine as the affected ninja targets should never be built.
 // That is handled above.
 //
 // A platform_bootclasspath module can use libraries from both platform and APEX variants. Unlike
 // the bootclasspath_fragment it supports dex_import modules which provides the dex file. So, it
 // can obtain a boot dex jar from a prebuilt that is not part of an APEX. However, it is assumed
 // that if the library can be part of an APEX then it is the APEX variant that is used.
 //
 // This check handles the slightly different requirements of the bootclasspath_fragment and
 // platform_bootclasspath modules by only deferring error reporting for the platform variant of
 // a prebuilt modules that has other variants which are part of an APEX.
 //
 // TODO(b/187910671): Remove this once platform variants are no longer created unnecessarily.
 if android.IsModulePrebuilt(ctx, module) {
  // An inactive source module can still contribute to the APEX but an inactive prebuilt module
  // should not contribute to anything. So, rather than have a missing dex jar cause a Soong
  // failure defer the error reporting to Ninja. Unless the prebuilt build target is explicitly
  // built Ninja should never use the dex jar file.
  if !isActiveModule(ctx, module) {
   return true
  }

  if am, ok := module.(android.ApexModule); ok && am.InAnyApex() {
   apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
   if apexInfo.IsForPlatform() {
    return true
   }
  }
 }

 return false
}

// handleMissingDexBootFile will either log a warning or create an error rule to create the fake
// file depending on the value returned from deferReportingMissingBootDexJar.
func handleMissingDexBootFile(ctx android.ModuleContext, module android.ModuleOrProxy, fake android.WritablePath, reason string) {
 if deferReportingMissingBootDexJar(ctx, module) {
  // Create an error rule that pretends to create the output file but will actually fail if it
  // is run.
  android.ErrorRule(ctx, fake, fmt.Sprintf("missing boot dex jar dependency for %s: %s", module, reason))
 } else {
  ctx.ModuleErrorf("module %s does not provide a dex jar: %s", module, reason)
 }
}

// retrieveEncodedBootDexJarFromModule returns a path to the boot dex jar from the supplied module's
// DexJarBuildPath() method.
//
// The returned path will usually be to a dex jar file that has been encoded with hidden API flags.
// However, under certain conditions, e.g. errors, or special build configurations it will return
// a path to a fake file.
func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module android.ModuleProxy) android.Path {
 info := android.OtherModuleProviderOrDefault(ctx, module, JavaInfoProvider)
 if !info.DexJarBuildPath.Valid() {
  fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name()))
  handleMissingDexBootFile(ctx, module, fake, info.DexJarBuildPath.InvalidReason())
  return fake
 }
 return info.DexJarBuildPath.Path()
}

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