Unify handling of compat and normal libs in class loader contexts.
Also, add tests for compatibility libraries in class loader context.
This CL separates special-case handling of compatibility libraries into
a "fixup" step that is done after class loader context is constructed by
Soong. This allows to handle compatibility libraries and normal
libraries uniformly, which should enable further simplification of class
loader context representation (in subsequent CLs).
Currently the only "fixup" step is removal of libraries from conditional
class loader context if they already are in unconditional context. This
check cannot be done at the time when the libraries are added to
conditional context, because the full uncoditional context is not yet
known at that time. Previously construction of unconditional context was
delayed, now it is no longer delayed and handled in the same way as
unconditional context, and the "fixup" does the filtering.
Test: lunch aosp_cf_x86_phone-userdebug && m
Bug: 132357300
Change-Id: Ie71e9fb2d3d529b5317cd84e09ab3c853017c349
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 814b75d..903677f 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -195,6 +195,9 @@
}
type classLoaderContext struct {
+ // Library names
+ Names []string
+
// The class loader context using paths in the build.
Host android.Paths
@@ -209,7 +212,7 @@
// targetSdkVersion in the manifest or APK is less than that API version.
type classLoaderContextMap map[int]*classLoaderContext
-const anySdkVersion int = 9999 // should go last in class loader context
+const AnySdkVersion int = 9999 // should go last in class loader context
func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
if _, ok := m[sdkVer]; !ok {
@@ -218,14 +221,19 @@
return m[sdkVer]
}
+func (clc *classLoaderContext) addLib(lib string, hostPath android.Path, targetPath string) {
+ clc.Names = append(clc.Names, lib)
+ clc.Host = append(clc.Host, hostPath)
+ clc.Target = append(clc.Target, targetPath)
+}
+
func (m classLoaderContextMap) addLibs(ctx android.PathContext, sdkVer int, module *ModuleConfig, libs ...string) bool {
clc := m.getValue(sdkVer)
for _, lib := range libs {
if p, ok := module.LibraryPaths[lib]; ok && p.Host != nil && p.Device != UnknownInstallLibraryPath {
- clc.Host = append(clc.Host, p.Host)
- clc.Target = append(clc.Target, p.Device)
+ clc.addLib(lib, p.Host, p.Device)
} else {
- if sdkVer == anySdkVersion {
+ if sdkVer == AnySdkVersion {
// Fail the build if dexpreopt doesn't know paths to one of the <uses-library>
// dependencies. In the future we may need to relax this and just disable dexpreopt.
android.ReportPathErrorf(ctx, "dexpreopt cannot find path for <uses-library> '%s'", lib)
@@ -239,11 +247,17 @@
return true
}
+func (m classLoaderContextMap) usesLibs() []string {
+ if clc, ok := m[AnySdkVersion]; ok {
+ return clc.Names
+ }
+ return nil
+}
+
func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
clc := m.getValue(sdkVer)
for _, lib := range libs {
- clc.Host = append(clc.Host, SystemServerDexJarHostPath(ctx, lib))
- clc.Target = append(clc.Target, filepath.Join("/system/framework", lib+".jar"))
+ clc.addLib(lib, SystemServerDexJarHostPath(ctx, lib), filepath.Join("/system/framework", lib+".jar"))
}
}
@@ -261,7 +275,7 @@
// as the runtime classpath won't match and the dexpreopted code will be discarded. Therefore in
// such cases the function returns nil, which disables dexpreopt.
//
-// 2. All other library jars or APKs for which the exact <uses-library> list is unknown. They use
+// 3. All other library jars or APKs for which the exact <uses-library> list is unknown. They use
// the unsafe &-classpath workaround that means empty class loader context and absence of runtime
// check that the class loader context provided by the PackageManager agrees with the stored
// class loader context recorded in the .odex file.
@@ -273,21 +287,19 @@
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.
- classLoaderContexts.addSystemServerLibs(anySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
+ classLoaderContexts.addSystemServerLibs(AnySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
} else if module.EnforceUsesLibraries {
// Unconditional class loader context.
usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
- if !classLoaderContexts.addLibs(ctx, anySdkVersion, module, usesLibs...) {
+ if !classLoaderContexts.addLibs(ctx, AnySdkVersion, module, usesLibs...) {
return nil
}
// Conditional class loader context for API version < 28.
const httpLegacy = "org.apache.http.legacy"
- if !contains(usesLibs, httpLegacy) {
- if !classLoaderContexts.addLibs(ctx, 28, module, httpLegacy) {
- return nil
- }
+ if !classLoaderContexts.addLibs(ctx, 28, module, httpLegacy) {
+ return nil
}
// Conditional class loader context for API version < 29.
@@ -301,10 +313,8 @@
// Conditional class loader context for API version < 30.
const testBase = "android.test.base"
- if !contains(usesLibs, testBase) {
- if !classLoaderContexts.addLibs(ctx, 30, module, testBase) {
- return nil
- }
+ if !classLoaderContexts.addLibs(ctx, 30, module, testBase) {
+ return nil
}
} else {
@@ -314,9 +324,32 @@
// to the &.
}
+ fixConditionalClassLoaderContext(classLoaderContexts)
+
return &classLoaderContexts
}
+// Now that the full unconditional context is known, reconstruct conditional context.
+// Apply filters for individual libraries, mirroring what the PackageManager does when it
+// constructs class loader context on device.
+func fixConditionalClassLoaderContext(clcMap classLoaderContextMap) {
+ usesLibs := clcMap.usesLibs()
+
+ for sdkVer, clc := range clcMap {
+ if sdkVer == AnySdkVersion {
+ continue
+ }
+ clcMap[sdkVer] = &classLoaderContext{}
+ for i, lib := range clc.Names {
+ if android.InList(lib, usesLibs) {
+ // skip compatibility libraries that are already included in unconditional context
+ } else {
+ clcMap[sdkVer].addLib(lib, clc.Host[i], clc.Target[i])
+ }
+ }
+ }
+}
+
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) {
@@ -363,7 +396,7 @@
checkSystemServerOrder(ctx, jarIndex)
- clc := classLoaderContexts[anySdkVersion]
+ clc := classLoaderContexts[AnySdkVersion]
rule.Command().
Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clc.Host.Strings(), ":") + "]").
Implicits(clc.Host).
@@ -391,14 +424,15 @@
Text(`eval "$(`).Tool(globalSoong.ConstructContext).
Text(` --target-sdk-version ${target_sdk_version}`)
for _, ver := range android.SortedIntKeys(classLoaderContexts) {
- clc := classLoaderContexts.getValue(ver)
- verString := fmt.Sprintf("%d", ver)
- if ver == anySdkVersion {
- verString = "any" // a special keyword that means any SDK version
+ if clc := classLoaderContexts.getValue(ver); len(clc.Host) > 0 {
+ verString := fmt.Sprintf("%d", ver)
+ if ver == AnySdkVersion {
+ verString = "any" // a special keyword that means any SDK version
+ }
+ cmd.Textf(`--host-classpath-for-sdk %s %s`, verString, strings.Join(clc.Host.Strings(), ":")).
+ Implicits(clc.Host).
+ Textf(`--target-classpath-for-sdk %s %s`, verString, strings.Join(clc.Target, ":"))
}
- cmd.Textf(`--host-classpath-for-sdk %s %s`, verString, strings.Join(clc.Host.Strings(), ":")).
- Implicits(clc.Host).
- Textf(`--target-classpath-for-sdk %s %s`, verString, strings.Join(clc.Target, ":"))
}
cmd.Text(`)"`)
} else {