Merge "Simplify the construction of class loader contexts for system server jars."
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 6cb9873..7dd01e2 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -41,21 +41,12 @@
 
 	"android/soong/android"
 
-	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
 )
 
 const SystemPartition = "/system/"
 const SystemOtherPartition = "/system_other/"
 
-type dependencyTag struct {
-	blueprint.BaseDependencyTag
-	name string
-}
-
-var SystemServerDepTag = dependencyTag{name: "system-server-dep"}
-var SystemServerForcedDepTag = dependencyTag{name: "system-server-forced-dep"}
-
 // GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
 // ModuleConfig.  The produced files and their install locations will be available through rule.Installs().
 func GenerateDexpreoptRule(ctx android.PathContext, globalSoong *GlobalSoongConfig,
@@ -116,13 +107,6 @@
 		}
 	}
 
-	// Don't preopt system server jars that are not Soong modules.
-	if android.InList(module.Name, NonUpdatableSystemServerJars(ctx, global)) {
-		if _, ok := ctx.(android.ModuleContext); !ok {
-			return true
-		}
-	}
-
 	// If OnlyPreoptBootImageAndSystemServer=true and module is not in boot class path skip
 	// Also preopt system server jars since selinux prevents system server from loading anything from
 	// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
@@ -239,6 +223,8 @@
 
 	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
 
+	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
+
 	// The class loader context using paths in the build
 	var classLoaderContextHost android.Paths
 
@@ -253,8 +239,8 @@
 	var conditionalClassLoaderContextHost29 android.Paths
 	var conditionalClassLoaderContextTarget29 []string
 
-	var classLoaderContextHostString, classLoaderContextDeviceString string
-	var classLoaderDeps android.Paths
+	// A flag indicating if the '&' class loader context is used.
+	unknownClassLoaderContext := false
 
 	if module.EnforceUsesLibraries {
 		usesLibs := append(copyOf(module.UsesLibraries), module.PresentOptionalUsesLibraries...)
@@ -298,49 +284,38 @@
 			pathForLibrary(module, hidlBase))
 		conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29,
 			filepath.Join("/system/framework", hidlBase+".jar"))
-
-		classLoaderContextHostString = strings.Join(classLoaderContextHost.Strings(), ":")
-	} else if android.InList(module.Name, NonUpdatableSystemServerJars(ctx, global)) {
-		// We expect that all dexpreopted system server jars are Soong modules.
-		mctx, isModule := ctx.(android.ModuleContext)
-		if !isModule {
-			panic("Cannot dexpreopt system server jar that is not a soong module.")
+	} else if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
+		// System server jars should be dexpreopted together: class loader context of each jar
+		// should include all preceding jars on the system server classpath.
+		for _, otherJar := range systemServerJars[:jarIndex] {
+			classLoaderContextHost = append(classLoaderContextHost, SystemServerDexJarHostPath(ctx, otherJar))
+			classLoaderContextTarget = append(classLoaderContextTarget, "/system/framework/"+otherJar+".jar")
 		}
 
-		// System server jars should be dexpreopted together: class loader context of each jar
-		// should include preceding jars (which can be found as dependencies of the current jar
-		// with a special tag).
-		var jarsOnHost android.Paths
-		var jarsOnDevice []string
-		mctx.VisitDirectDepsWithTag(SystemServerDepTag, func(dep android.Module) {
-			depName := mctx.OtherModuleName(dep)
-			if jar, ok := dep.(interface{ DexJar() android.Path }); ok {
-				jarsOnHost = append(jarsOnHost, jar.DexJar())
-				jarsOnDevice = append(jarsOnDevice, "/system/framework/"+depName+".jar")
-			} else {
-				mctx.ModuleErrorf("module \"%s\" is not a jar", depName)
-			}
-		})
-		classLoaderContextHostString = strings.Join(jarsOnHost.Strings(), ":")
-		classLoaderContextDeviceString = strings.Join(jarsOnDevice, ":")
-		classLoaderDeps = jarsOnHost
+		// Copy the system server jar to a predefined location where dex2oat will find it.
+		dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
+		rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
+		rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost)
 	} else {
 		// Pass special class loader context to skip the classpath and collision check.
 		// This will get removed once LOCAL_USES_LIBRARIES is enforced.
 		// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
 		// to the &.
-		classLoaderContextHostString = `\&`
+		unknownClassLoaderContext = true
 	}
 
 	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
 	rule.Command().FlagWithOutput("rm -f ", odexPath)
 	// Set values in the environment of the rule.  These may be modified by construct_context.sh.
-	if classLoaderContextHostString == `\&` {
-		rule.Command().Text(`class_loader_context_arg=--class-loader-context=\&`)
-		rule.Command().Text(`stored_class_loader_context_arg=""`)
+	if unknownClassLoaderContext {
+		rule.Command().
+			Text(`class_loader_context_arg=--class-loader-context=\&`).
+			Text(`stored_class_loader_context_arg=""`)
 	} else {
-		rule.Command().Text("class_loader_context_arg=--class-loader-context=PCL[" + classLoaderContextHostString + "]")
-		rule.Command().Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + classLoaderContextDeviceString + "]")
+		rule.Command().
+			Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(classLoaderContextHost.Strings(), ":") + "]").
+			Implicits(classLoaderContextHost).
+			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(classLoaderContextTarget, ":") + "]")
 	}
 
 	if module.EnforceUsesLibraries {
@@ -395,7 +370,7 @@
 		Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", module.PreoptBootClassPathDexFiles, ":").
 		Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":").
 		Flag("${class_loader_context_arg}").
-		Flag("${stored_class_loader_context_arg}").Implicits(classLoaderDeps).
+		Flag("${stored_class_loader_context_arg}").
 		FlagWithArg("--boot-image=", strings.Join(module.DexPreoptImageLocations, ":")).Implicits(module.DexPreoptImagesDeps[archIdx].Paths()).
 		FlagWithInput("--dex-file=", module.DexPath).
 		FlagWithArg("--dex-location=", dexLocationArg).
@@ -609,6 +584,14 @@
 	}).([]string)
 }
 
+// A predefined location for the system server dex jars. This is needed in order to generate
+// class loader context for dex2oat, as the path to the jar in the Soong module may be unknown
+// at that time (Soong processes the jars in dependency order, which may be different from the
+// the system server classpath order).
+func SystemServerDexJarHostPath(ctx android.PathContext, jar string) android.OutputPath {
+	return android.PathForOutput(ctx, ctx.Config().DeviceName(), "system_server_dexjars", jar+".jar")
+}
+
 func contains(l []string, s string) bool {
 	for _, e := range l {
 		if e == s {
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index d77d3e1..ffce2a9 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -30,9 +30,9 @@
 	return ctx.Config().OnceStringSlice(systemServerClasspathKey, func() []string {
 		global := dexpreopt.GetGlobalConfig(ctx)
 		var systemServerClasspathLocations []string
-		var dexpreoptJars = *DexpreoptedSystemServerJars(ctx.Config())
-		// 1) The jars that are dexpreopted.
-		for _, m := range dexpreoptJars {
+		nonUpdatable := dexpreopt.NonUpdatableSystemServerJars(ctx, global)
+		// 1) Non-updatable jars.
+		for _, m := range nonUpdatable {
 			systemServerClasspathLocations = append(systemServerClasspathLocations,
 				filepath.Join("/system/framework", m+".jar"))
 		}
@@ -41,13 +41,6 @@
 			systemServerClasspathLocations = append(systemServerClasspathLocations,
 				dexpreopt.GetJarLocationFromApexJarPair(m))
 		}
-		// 3) The jars from make (which are not updatable, not preopted).
-		for _, m := range dexpreopt.NonUpdatableSystemServerJars(ctx, global) {
-			if !android.InList(m, dexpreoptJars) {
-				systemServerClasspathLocations = append(systemServerClasspathLocations,
-					filepath.Join("/system/framework", m+".jar"))
-			}
-		}
 		if len(systemServerClasspathLocations) != len(global.SystemServerJars)+len(global.UpdatableSystemServerJars) {
 			panic(fmt.Errorf("Wrong number of system server jars, got %d, expected %d",
 				len(systemServerClasspathLocations),
diff --git a/java/java.go b/java/java.go
index 59a1e05..22d14ec 100644
--- a/java/java.go
+++ b/java/java.go
@@ -23,14 +23,12 @@
 	"path/filepath"
 	"strconv"
 	"strings"
-	"sync"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
-	"android/soong/dexpreopt"
 	"android/soong/java/config"
 	"android/soong/tradefed"
 )
@@ -60,8 +58,6 @@
 			PropertyName: "java_tests",
 		},
 	})
-
-	android.PostDepsMutators(RegisterPostDepsMutators)
 }
 
 func RegisterJavaBuildComponents(ctx android.RegistrationContext) {
@@ -90,44 +86,6 @@
 	ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
 }
 
-func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.BottomUp("ordered_system_server_jars", systemServerJarsDepsMutator)
-}
-
-var (
-	dexpreoptedSystemServerJarsKey  = android.NewOnceKey("dexpreoptedSystemServerJars")
-	dexpreoptedSystemServerJarsLock sync.Mutex
-)
-
-func DexpreoptedSystemServerJars(config android.Config) *[]string {
-	return config.Once(dexpreoptedSystemServerJarsKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
-}
-
-// A PostDepsMutator pass that enforces total order on non-updatable system server jars. A total
-// order is neededed because such jars must be dexpreopted together (each jar on the list must have
-// all preceding jars in its class loader context). The total order must be compatible with the
-// partial order imposed by genuine dependencies between system server jars (which is not always
-// respected by the PRODUCT_SYSTEM_SERVER_JARS variable).
-//
-// An earlier mutator pass creates genuine dependencies, and this pass traverses the jars in that
-// order (which is partial and non-deterministic). This pass adds additional dependencies between
-// jars, making the order total and deterministic. It also constructs a global ordered list.
-func systemServerJarsDepsMutator(ctx android.BottomUpMutatorContext) {
-	jars := dexpreopt.NonUpdatableSystemServerJars(ctx, dexpreopt.GetGlobalConfig(ctx))
-	name := ctx.ModuleName()
-	if android.InList(name, jars) {
-		dexpreoptedSystemServerJarsLock.Lock()
-		defer dexpreoptedSystemServerJarsLock.Unlock()
-		jars := DexpreoptedSystemServerJars(ctx.Config())
-		for _, dep := range *jars {
-			ctx.AddDependency(ctx.Module(), dexpreopt.SystemServerDepTag, dep)
-		}
-		*jars = append(*jars, name)
-	}
-}
-
 func (j *Module) checkSdkVersion(ctx android.ModuleContext) {
 	if j.SocSpecific() || j.DeviceSpecific() ||
 		(j.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
@@ -711,11 +669,6 @@
 	} else if j.shouldInstrumentStatic(ctx) {
 		ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent")
 	}
-
-	// services depend on com.android.location.provider, but dependency in not registered in a Blueprint file
-	if ctx.ModuleName() == "services" {
-		ctx.AddDependency(ctx.Module(), dexpreopt.SystemServerForcedDepTag, "com.android.location.provider")
-	}
 }
 
 func hasSrcExt(srcs []string, ext string) bool {