Convert android.TransitionMutator to TransitionInfo

Use the ApexInfo instead of a string as the TransitionInfo for apex
variations.  This removes the need for apexInfoMutator, which is the
last remaining top down mutator.

This has a variety of ramifications.  One is that it is no longer
possible to add a dependency onto the apex variation of a module,
as that would require constructing a matching ApexInfo.  Instead,
anything that wants to add a dependency on the apex variation has
to depend on the apex instead, and get to the module by walking
its transistive dependencies.

Another ramification is that modules in apexes can no longer
determine which apexes they are in (unless they set
UniqueApexVariations so that each variation is in exactly one
apex).  This prevents some of the existing container violation
checks from working after this CL, tracked in b/394955484.

It also requires using unique variation names for the prebuilt
and source dependencies of apexes, so the apex variations
for dependencies of prebuilts now have a prebuilt_ prefix.

Bug: 372543712
Bug: 394955484
Test: go test ./...
Change-Id: I3d08aca1ac956ab0e343ec3f235a736cd93be0e1
diff --git a/android/Android.bp b/android/Android.bp
index d27a8fa..babc913 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -125,7 +125,6 @@
         "all_teams_test.go",
         "android_test.go",
         "androidmk_test.go",
-        "apex_test.go",
         "arch_test.go",
         "blueprint_e2e_test.go",
         "build_prop_test.go",
diff --git a/android/apex.go b/android/apex.go
index 4917149..68d0ce8 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"slices"
-	"sort"
 	"strconv"
 	"strings"
 	"sync"
@@ -55,18 +54,30 @@
 	// to true.
 	UsePlatformApis bool
 
-	// List of Apex variant names that this module is associated with. This initially is the
-	// same as the `ApexVariationName` field.  Then when multiple apex variants are merged in
-	// mergeApexVariations, ApexInfo struct of the merged variant holds the list of apexBundles
-	// that are merged together.
-	InApexVariants []string
-
 	// True if this is for a prebuilt_apex.
 	//
 	// If true then this will customize the apex processing to make it suitable for handling
 	// prebuilt_apex, e.g. it will prevent ApexInfos from being merged together.
 	//
-	// See Prebuilt.ApexInfoMutator for more information.
+	// Unlike the source apex module type the prebuilt_apex module type cannot share compatible variants
+	// across prebuilt_apex modules. That is because there is no way to determine whether two
+	// prebuilt_apex modules that export files for the same module are compatible. e.g. they could have
+	// been built from different source at different times or they could have been built with different
+	// build options that affect the libraries.
+	//
+	// While it may be possible to provide sufficient information to determine whether two prebuilt_apex
+	// modules were compatible it would be a lot of work and would not provide much benefit for a couple
+	// of reasons:
+	//   - The number of prebuilt_apex modules that will be exporting files for the same module will be
+	//     low as the prebuilt_apex only exports files for the direct dependencies that require it and
+	//     very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a
+	//     few com.android.art* apex files that contain the same contents and could export files for the
+	//     same modules but only one of them needs to do so. Contrast that with source apex modules which
+	//     need apex specific variants for every module that contributes code to the apex, whether direct
+	//     or indirect.
+	//   - The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
+	//     extra copying of files. Contrast that with source apex modules that has to build each variant
+	//     from source.
 	ForPrebuiltApex bool
 
 	// Returns the name of the overridden apex (com.android.foo)
@@ -74,24 +85,36 @@
 
 	// Returns the value of `apex_available_name`
 	ApexAvailableName string
+}
 
+func (a ApexInfo) Variation() string {
+	return a.ApexVariationName
+}
+
+// Minimize is called during a transition from a module with a unique variation per apex to a module that should
+// share variations between apexes.  It returns a minimized ApexInfo that removes any apex names and replaces
+// the variation name with one computed from the remaining properties.
+func (a ApexInfo) Minimize() ApexInfo {
+	info := ApexInfo{
+		MinSdkVersion:   a.MinSdkVersion,
+		UsePlatformApis: a.UsePlatformApis,
+	}
+	info.ApexVariationName = info.mergedName()
+	return info
+}
+
+type ApexAvailableInfo struct {
 	// Returns the apex names that this module is available for
 	ApexAvailableFor []string
 }
 
-// AllApexInfo holds the ApexInfo of all apexes that include this module.
-type AllApexInfo struct {
-	ApexInfos []ApexInfo
-}
-
 var ApexInfoProvider = blueprint.NewMutatorProvider[ApexInfo]("apex_mutate")
-var AllApexInfoProvider = blueprint.NewMutatorProvider[*AllApexInfo]("apex_info")
+var ApexAvailableInfoProvider = blueprint.NewMutatorProvider[ApexAvailableInfo]("apex_mutate")
 
 func (i ApexInfo) AddJSONData(d *map[string]interface{}) {
 	(*d)["Apex"] = map[string]interface{}{
 		"ApexVariationName": i.ApexVariationName,
 		"MinSdkVersion":     i.MinSdkVersion,
-		"InApexVariants":    i.InApexVariants,
 		"ForPrebuiltApex":   i.ForPrebuiltApex,
 	}
 }
@@ -117,32 +140,20 @@
 	return i.ApexVariationName == ""
 }
 
-// InApexVariant tells whether this apex variant of the module is part of the given apexVariant or
-// not.
-func (i ApexInfo) InApexVariant(apexVariant string) bool {
-	for _, a := range i.InApexVariants {
-		if a == apexVariant {
-			return true
-		}
-	}
-	return false
-}
-
 // To satisfy the comparable interface
 func (i ApexInfo) Equal(other any) bool {
 	otherApexInfo, ok := other.(ApexInfo)
 	return ok && i.ApexVariationName == otherApexInfo.ApexVariationName &&
 		i.MinSdkVersion == otherApexInfo.MinSdkVersion &&
 		i.Updatable == otherApexInfo.Updatable &&
-		i.UsePlatformApis == otherApexInfo.UsePlatformApis &&
-		slices.Equal(i.InApexVariants, otherApexInfo.InApexVariants)
+		i.UsePlatformApis == otherApexInfo.UsePlatformApis
 }
 
 // ApexBundleInfo contains information about the dependencies of an apex
 type ApexBundleInfo struct {
 }
 
-var ApexBundleInfoProvider = blueprint.NewMutatorProvider[ApexBundleInfo]("apex_info")
+var ApexBundleInfoProvider = blueprint.NewMutatorProvider[ApexBundleInfo]("apex_mutate")
 
 // DepIsInSameApex defines an interface that should be used to determine whether a given dependency
 // should be considered as part of the same APEX as the current module or not. Note: this was
@@ -315,6 +326,61 @@
 	apexInfosLock sync.Mutex // protects apexInfos during parallel apexInfoMutator
 }
 
+func (m *ApexModuleBase) ApexTransitionMutatorSplit(ctx BaseModuleContext) []ApexInfo {
+	return []ApexInfo{{}}
+}
+
+func (m *ApexModuleBase) ApexTransitionMutatorOutgoing(ctx OutgoingTransitionContext, info ApexInfo) ApexInfo {
+	if !ctx.Module().(DepIsInSameApex).OutgoingDepIsInSameApex(ctx.DepTag()) {
+		return ApexInfo{}
+	}
+	return info
+}
+
+func (m *ApexModuleBase) ApexTransitionMutatorIncoming(ctx IncomingTransitionContext, info ApexInfo) ApexInfo {
+	module := ctx.Module().(ApexModule)
+	if !module.CanHaveApexVariants() {
+		return ApexInfo{}
+	}
+
+	if !ctx.Module().(DepIsInSameApex).IncomingDepIsInSameApex(ctx.DepTag()) {
+		return ApexInfo{}
+	}
+
+	if info.ApexVariationName == "" {
+		return ApexInfo{}
+	}
+
+	if !ctx.Module().(ApexModule).UniqueApexVariations() && !m.ApexProperties.UniqueApexVariationsForDeps && !info.ForPrebuiltApex {
+		return info.Minimize()
+	}
+	return info
+}
+
+func (m *ApexModuleBase) ApexTransitionMutatorMutate(ctx BottomUpMutatorContext, info ApexInfo) {
+	SetProvider(ctx, ApexInfoProvider, info)
+
+	module := ctx.Module().(ApexModule)
+	base := module.apexModuleBase()
+
+	platformVariation := info.ApexVariationName == ""
+	if !platformVariation {
+		// Do some validity checks.
+		// TODO(jiyong): is this the right place?
+		base.checkApexAvailableProperty(ctx)
+
+		SetProvider(ctx, ApexAvailableInfoProvider, ApexAvailableInfo{
+			ApexAvailableFor: module.ApexAvailableFor(),
+		})
+	}
+	if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) && module.NotAvailableForPlatform() {
+		// Do not install the module for platform, but still allow it to output
+		// uninstallable AndroidMk entries in certain cases when they have side
+		// effects.  TODO(jiyong): move this routine to somewhere else
+		module.MakeUninstallable()
+	}
+}
+
 // Initializes ApexModuleBase struct. Not calling this (even when inheriting from ApexModuleBase)
 // prevents the module from being mutated for apexBundle.
 func InitApexModule(m ApexModule) {
@@ -514,195 +580,14 @@
 	return true
 }
 
-// mergeApexVariations deduplicates apex variations that would build identically into a common
-// variation. It returns the reduced list of variations and a list of aliases from the original
-// variation names to the new variation names.
-func mergeApexVariations(apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
-	seen := make(map[string]int)
-	for _, apexInfo := range apexInfos {
-		// If this is for a prebuilt apex then use the actual name of the apex variation to prevent this
-		// from being merged with other ApexInfo. See Prebuilt.ApexInfoMutator for more information.
-		if apexInfo.ForPrebuiltApex {
-			merged = append(merged, apexInfo)
-			continue
-		}
-
-		// Merge the ApexInfo together. If a compatible ApexInfo exists then merge the information from
-		// this one into it, otherwise create a new merged ApexInfo from this one and save it away so
-		// other ApexInfo instances can be merged into it.
-		variantName := apexInfo.ApexVariationName
-		mergedName := apexInfo.mergedName()
-		if index, exists := seen[mergedName]; exists {
-			// Variants having the same mergedName are deduped
-			merged[index].InApexVariants = append(merged[index].InApexVariants, variantName)
-			merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
-			// Platform APIs is allowed for this module only when all APEXes containing
-			// the module are with `use_platform_apis: true`.
-			merged[index].UsePlatformApis = merged[index].UsePlatformApis && apexInfo.UsePlatformApis
-		} else {
-			seen[mergedName] = len(merged)
-			apexInfo.ApexVariationName = mergedName
-			apexInfo.InApexVariants = CopyOf(apexInfo.InApexVariants)
-			merged = append(merged, apexInfo)
-		}
-		aliases = append(aliases, [2]string{variantName, mergedName})
-	}
-	return merged, aliases
-}
-
-// IncomingApexTransition is called by apexTransitionMutator.IncomingTransition on modules that can be in apexes.
-// The incomingVariation can be either the name of an apex if the dependency is coming directly from an apex
-// module, or it can be the name of an apex variation (e.g. apex10000) if it is coming from another module that
-// is in the apex.
-func IncomingApexTransition(ctx IncomingTransitionContext, incomingVariation string) string {
-	module := ctx.Module().(ApexModule)
-	base := module.apexModuleBase()
-
-	var apexInfos []ApexInfo
-	if allApexInfos, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
-		apexInfos = allApexInfos.ApexInfos
-	}
-
-	// Dependencies from platform variations go to the platform variation.
-	if incomingVariation == "" {
-		return ""
-	}
-
-	if len(apexInfos) == 0 {
-		if ctx.IsAddingDependency() {
-			// If this module has no apex variations we can't do any mapping on the incoming variation, just return it
-			// and let the caller get a "missing variant" error.
-			return incomingVariation
-		} else {
-			// If this module has no apex variations the use the platform variation.
-			return ""
-		}
-	}
-
-	// Convert the list of apex infos into from the AllApexInfoProvider into the merged list
-	// of apex variations and the aliases from apex names to apex variations.
-	var aliases [][2]string
-	if !module.UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
-		apexInfos, aliases = mergeApexVariations(apexInfos)
-	}
-
-	// Check if the incoming variation matches an apex name, and if so use the corresponding
-	// apex variation.
-	aliasIndex := slices.IndexFunc(aliases, func(alias [2]string) bool {
-		return alias[0] == incomingVariation
-	})
-	if aliasIndex >= 0 {
-		return aliases[aliasIndex][1]
-	}
-
-	// Check if the incoming variation matches an apex variation.
-	apexIndex := slices.IndexFunc(apexInfos, func(info ApexInfo) bool {
-		return info.ApexVariationName == incomingVariation
-	})
-	if apexIndex >= 0 {
-		return incomingVariation
-	}
-
-	return ""
-}
-
-func MutateApexTransition(ctx BaseModuleContext, variation string) {
-	module := ctx.Module().(ApexModule)
-	base := module.apexModuleBase()
-	platformVariation := variation == ""
-
-	var apexInfos []ApexInfo
-	if allApexInfos, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
-		apexInfos = allApexInfos.ApexInfos
-	}
-
-	if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) && module.NotAvailableForPlatform() {
-		// Do not install the module for platform, but still allow it to output
-		// uninstallable AndroidMk entries in certain cases when they have side
-		// effects.  TODO(jiyong): move this routine to somewhere else
-		module.MakeUninstallable()
-	}
-
-	// Do some validity checks.
-	// TODO(jiyong): is this the right place?
-	base.checkApexAvailableProperty(ctx)
-
-	// Shortcut
-	if len(apexInfos) == 0 {
-		return
-	}
-
-	if !module.UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
-		apexInfos, _ = mergeApexVariations(apexInfos)
-	}
-
-	if !platformVariation {
-		var thisApexInfo ApexInfo
-
-		apexIndex := slices.IndexFunc(apexInfos, func(info ApexInfo) bool {
-			return info.ApexVariationName == variation
-		})
-		if apexIndex >= 0 {
-			thisApexInfo = apexInfos[apexIndex]
-		} else {
-			panic(fmt.Errorf("failed to find apexInfo for incoming variation %q", variation))
-		}
-		thisApexInfo.ApexAvailableFor = module.ApexAvailableFor()
-
-		SetProvider(ctx, ApexInfoProvider, thisApexInfo)
-	}
-}
-
-func ApexInfoMutator(ctx TopDownMutatorContext, module ApexModule) {
-	base := module.apexModuleBase()
-	if len(base.apexInfos) > 0 {
-		apexInfos := slices.Clone(base.apexInfos)
-		slices.SortFunc(apexInfos, func(a, b ApexInfo) int {
-			return strings.Compare(a.ApexVariationName, b.ApexVariationName)
-		})
-		SetProvider(ctx, AllApexInfoProvider, &AllApexInfo{apexInfos})
-		// base.apexInfos is only needed to propagate the list of apexes from the apex module to its
-		// contents within apexInfoMutator. Clear it so it doesn't accidentally get used later.
-		base.apexInfos = nil
-	}
-}
-
 // UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies that are
 // in the same APEX have unique APEX variations so that the module can link against the right
 // variant.
 func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModule) {
-	// anyInSameApex returns true if the two ApexInfo lists contain any values in an
-	// InApexVariants list in common. It is used instead of OutgoingDepIsInSameApex because it needs to
-	// determine if the dep is in the same APEX due to being directly included, not only if it
-	// is included _because_ it is a dependency.
-	anyInSameApex := func(a, b ApexModule) bool {
-		collectApexes := func(m ApexModule) []string {
-			if allApexInfo, ok := OtherModuleProvider(mctx, m, AllApexInfoProvider); ok {
-				var ret []string
-				for _, info := range allApexInfo.ApexInfos {
-					ret = append(ret, info.InApexVariants...)
-				}
-				return ret
-			}
-			return nil
-		}
-
-		aApexes := collectApexes(a)
-		bApexes := collectApexes(b)
-		sort.Strings(bApexes)
-		for _, aApex := range aApexes {
-			index := sort.SearchStrings(bApexes, aApex)
-			if index < len(bApexes) && bApexes[index] == aApex {
-				return true
-			}
-		}
-		return false
-	}
-
 	// If any of the dependencies requires unique apex variations, so does this module.
 	mctx.VisitDirectDeps(func(dep Module) {
 		if depApexModule, ok := dep.(ApexModule); ok {
-			if anyInSameApex(depApexModule, am) &&
+			if IsDepInSameApex(mctx, am, depApexModule) &&
 				(depApexModule.UniqueApexVariations() ||
 					depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) {
 				am.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps = true
@@ -849,8 +734,14 @@
 	})
 }
 
+type MinSdkVersionFromValueContext interface {
+	Config() Config
+	DeviceConfig() DeviceConfig
+	ModuleErrorContext
+}
+
 // Construct ApiLevel object from min_sdk_version string value
-func MinSdkVersionFromValue(ctx EarlyModuleContext, value string) ApiLevel {
+func MinSdkVersionFromValue(ctx MinSdkVersionFromValueContext, value string) ApiLevel {
 	if value == "" {
 		return NoneApiLevel
 	}
diff --git a/android/apex_test.go b/android/apex_test.go
deleted file mode 100644
index acc195d..0000000
--- a/android/apex_test.go
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright 2020 Google Inc. All rights reserved.
-//
-// 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 android
-
-import (
-	"reflect"
-	"testing"
-)
-
-func Test_mergeApexVariations(t *testing.T) {
-	const (
-		ForPrebuiltApex    = true
-		NotForPrebuiltApex = false
-	)
-	tests := []struct {
-		name        string
-		in          []ApexInfo
-		wantMerged  []ApexInfo
-		wantAliases [][2]string
-	}{
-		{
-			name: "single",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-			},
-		},
-		{
-			name: "merge",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo", "bar"},
-				}},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-				{"bar", "apex10000"},
-			},
-		},
-		{
-			name: "don't merge version",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     uncheckedFinalApiLevel(30),
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "apex30",
-					MinSdkVersion:     uncheckedFinalApiLevel(30),
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-				{"bar", "apex30"},
-			},
-		},
-		{
-			name: "merge updatable",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"foo", "bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-				{"bar", "apex10000"},
-			},
-		},
-		{
-			name: "don't merge when for prebuilt_apex",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				// This one should not be merged in with the others because it is for
-				// a prebuilt_apex.
-				{
-					ApexVariationName: "baz",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"baz"},
-					ForPrebuiltApex:   ForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"foo", "bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "baz",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"baz"},
-					ForPrebuiltApex:   ForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-				{"bar", "apex10000"},
-			},
-		},
-		{
-			name: "don't merge different UsePlatformApis",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     FutureApiLevel,
-					UsePlatformApis:   true,
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "apex10000_p",
-					MinSdkVersion:     FutureApiLevel,
-					UsePlatformApis:   true,
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-				{"bar", "apex10000_p"},
-			},
-		},
-		{
-			name: "merge same UsePlatformApis and allow using platform api",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					UsePlatformApis:   true,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     FutureApiLevel,
-					UsePlatformApis:   true,
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000_p",
-					MinSdkVersion:     FutureApiLevel,
-					UsePlatformApis:   true,
-					InApexVariants:    []string{"foo", "bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000_p"},
-				{"bar", "apex10000_p"},
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			gotMerged, gotAliases := mergeApexVariations(tt.in)
-			if !reflect.DeepEqual(gotMerged, tt.wantMerged) {
-				t.Errorf("mergeApexVariations() gotMerged = %v, want %v", gotMerged, tt.wantMerged)
-			}
-			if !reflect.DeepEqual(gotAliases, tt.wantAliases) {
-				t.Errorf("mergeApexVariations() gotAliases = %v, want %v", gotAliases, tt.wantAliases)
-			}
-		})
-	}
-}
diff --git a/android/api_levels.go b/android/api_levels.go
index 2b1d01d..b4fa251 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -311,7 +311,7 @@
 
 // ApiLevelFrom converts the given string `raw` to an ApiLevel.
 // If `raw` is invalid (empty string, unrecognized codename etc.) it returns an invalid ApiLevel
-func ApiLevelFrom(ctx PathContext, raw string) ApiLevel {
+func ApiLevelFrom(ctx ConfigContext, raw string) ApiLevel {
 	ret, err := ApiLevelFromUser(ctx, raw)
 	if err != nil {
 		return NewInvalidApiLevel(raw)
@@ -333,7 +333,7 @@
 //
 // Inputs that are not "current", known previews, or convertible to an integer
 // will return an error.
-func ApiLevelFromUser(ctx PathContext, raw string) (ApiLevel, error) {
+func ApiLevelFromUser(ctx ConfigContext, raw string) (ApiLevel, error) {
 	return ApiLevelFromUserWithConfig(ctx.Config(), raw)
 }
 
@@ -413,7 +413,7 @@
 // Converts an API level string `raw` into an ApiLevel in the same method as
 // `ApiLevelFromUser`, but the input is assumed to have no errors and any errors
 // will panic instead of returning an error.
-func ApiLevelOrPanic(ctx PathContext, raw string) ApiLevel {
+func ApiLevelOrPanic(ctx ConfigContext, raw string) ApiLevel {
 	value, err := ApiLevelFromUser(ctx, raw)
 	if err != nil {
 		panic(err.Error())
diff --git a/android/container.go b/android/container.go
index eb2fc18..5dc97d3 100644
--- a/android/container.go
+++ b/android/container.go
@@ -167,8 +167,8 @@
 }
 
 var apexContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
-	_, ok := ModuleProvider(mctx, AllApexInfoProvider)
-	return ok
+	// TODO(b/394955484): a module can't determine the apexes it belongs to any more
+	return false
 }
 
 var ctsContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
@@ -380,7 +380,7 @@
 
 func (c *ContainersInfo) ApexNames() (ret []string) {
 	for _, apex := range c.belongingApexes {
-		ret = append(ret, apex.InApexVariants...)
+		ret = append(ret, apex.BaseApexName)
 	}
 	slices.Sort(ret)
 	return ret
@@ -441,14 +441,10 @@
 		}
 	}
 
-	var belongingApexes []ApexInfo
-	if apexInfo, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
-		belongingApexes = apexInfo.ApexInfos
-	}
-
 	return ContainersInfo{
 		belongingContainers: containers,
-		belongingApexes:     belongingApexes,
+		// TODO(b/394955484): a module can't determine the apexes it belongs to any more
+		belongingApexes: nil,
 	}
 }
 
diff --git a/android/deapexer.go b/android/deapexer.go
index 4049d2b..6d00dcd 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -75,8 +75,6 @@
 
 	// map from the name of an exported file from a prebuilt_apex to the path to that file. The
 	// exported file name is the apex relative path, e.g. javalib/core-libart.jar.
-	//
-	// See Prebuilt.ApexInfoMutator for more information.
 	exports map[string]WritablePath
 
 	// name of the java libraries exported from the apex