Rework class loader context implementation.

The old representation consisted of a list of libraries (UsesLibraries),
a list of optional libraries (OptionalUsesLibraries) and a mapping from
library name to its build/install paths (LibraryPaths). The separation
into lists and map was necessary because of special handling of
compatibility libraries, which is now unified with normal libraries.

The new representation is a mapping from target SDK version to a tree
structure ClassLoaderContext. Each node of the tree represents a library
and contains library name, build/install paths and a slice of
subcontexts for dependencies. The same library may occur in the tree
multiple times in case it is a dependency of multiple libraries. The
order in which libraries are added matters (the resulting tree shape may
be different).

Test results have to be updated, as the resulting <uses-library> list is
reodered (previously it was a sorted list of map keys, and now it is
formed by a depth-first preorder traversal of the class loader tree).

Test: lunch aosp_cf_x86_phone-userdebug && m
Bug: 132357300
Bug: 168686456
Change-Id: I11be8cd2967f004fd58753d7c5fb99fed179cd63
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index a07f1fa..65380fe 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -81,16 +81,18 @@
 	}
 
 	if !dexpreoptDisabled(ctx, global, module) {
-		if clc, err := genClassLoaderContext(ctx, global, module); err != nil {
+		if valid, err := validateClassLoaderContext(module.ClassLoaderContexts); err != nil {
 			android.ReportPathErrorf(ctx, err.Error())
-		} else if clc != nil {
+		} else if valid {
+			fixClassLoaderContext(module.ClassLoaderContexts)
+
 			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
 				!module.NoCreateAppImage
 
 			generateDM := shouldGenerateDM(module, global)
 
 			for archIdx, _ := range module.Archs {
-				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, *clc, profile, appImage, generateDM)
+				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM)
 			}
 		}
 	}
@@ -197,8 +199,8 @@
 }
 
 func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
-	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, classLoaderContexts classLoaderContextMap,
-	profile android.WritablePath, appImage bool, generateDM bool) {
+	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
+	appImage bool, generateDM bool) {
 
 	arch := module.Archs[archIdx]
 
@@ -235,6 +237,16 @@
 	rule.Command().FlagWithOutput("rm -f ", odexPath)
 
 	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.
+
+		var clcHost android.Paths
+		var clcTarget []string
+		for _, lib := range systemServerJars[:jarIndex] {
+			clcHost = append(clcHost, SystemServerDexJarHostPath(ctx, lib))
+			clcTarget = append(clcTarget, filepath.Join("/system/framework", lib+".jar"))
+		}
+
 		// 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()))
@@ -242,11 +254,11 @@
 
 		checkSystemServerOrder(ctx, jarIndex)
 
-		clc := classLoaderContexts[AnySdkVersion]
 		rule.Command().
-			Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clc.Host.Strings(), ":") + "]").
-			Implicits(clc.Host).
-			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clc.Target, ":") + "]")
+			Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clcHost.Strings(), ":") + "]").
+			Implicits(clcHost).
+			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clcTarget, ":") + "]")
+
 	} else if module.EnforceUsesLibraries {
 		// Generate command that saves target SDK version in a shell variable.
 		if module.ManifestPath != nil {
@@ -266,13 +278,15 @@
 		}
 
 		// Generate command that saves host and target class loader context in shell variables.
-		clc, paths := computeClassLoaderContext(ctx, classLoaderContexts)
+		clc, paths := ComputeClassLoaderContext(module.ClassLoaderContexts)
 		cmd := rule.Command().
 			Text(`eval "$(`).Tool(globalSoong.ConstructContext).
 			Text(` --target-sdk-version ${target_sdk_version}`).
 			Text(clc).Implicits(paths)
 		cmd.Text(`)"`)
+
 	} else {
+		// Other libraries or APKs for which the exact <uses-library> list is unknown.
 		// 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