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
This commit is contained in:
Ulya Trafimovich
2020-10-07 15:05:21 +01:00
parent 7ccacaedbe
commit 24813e1d80
4 changed files with 134 additions and 84 deletions

View File

@@ -100,20 +100,30 @@ type GlobalSoongConfig struct {
ConstructContext android.Path ConstructContext android.Path
} }
// These libs are added as optional dependencies (<uses-library> with android:required set to false). // These libs are added as <uses-library> dependencies for apps if the targetSdkVersion in the
// This is because they haven't existed prior to certain SDK version, but classes in them were in // app manifest is less than the specified version. This is needed because these libraries haven't
// bootclasspath jars, etc. So making them hard dependencies (android:required=true) would prevent // existed prior to certain SDK version, but classes in them were in bootclasspath jars, etc.
// apps from being installed to such legacy devices. // Some of the compatibility libraries are optional (their <uses-library> tag has "required=false"),
var OptionalCompatUsesLibs = []string{ // so that if this library is missing this in not a build or run-time error.
"org.apache.http.legacy", var OrgApacheHttpLegacy = "org.apache.http.legacy"
"android.test.base", var AndroidTestBase = "android.test.base"
"android.test.mock", var AndroidTestMock = "android.test.mock"
} var AndroidHidlBase = "android.hidl.base-V1.0-java"
var AndroidHidlManager = "android.hidl.manager-V1.0-java"
var CompatUsesLibs = []string{ var OptionalCompatUsesLibs28 = []string{
"android.hidl.base-V1.0-java", OrgApacheHttpLegacy,
"android.hidl.manager-V1.0-java",
} }
var OptionalCompatUsesLibs30 = []string{
AndroidTestBase,
AndroidTestMock,
}
var CompatUsesLibs29 = []string{
AndroidHidlBase,
AndroidHidlManager,
}
var OptionalCompatUsesLibs = append(android.CopyOf(OptionalCompatUsesLibs28), OptionalCompatUsesLibs30...)
var CompatUsesLibs = android.CopyOf(CompatUsesLibs29)
const UnknownInstallLibraryPath = "error" const UnknownInstallLibraryPath = "error"

View File

@@ -195,6 +195,9 @@ func bootProfileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig,
} }
type classLoaderContext struct { type classLoaderContext struct {
// Library names
Names []string
// The class loader context using paths in the build. // The class loader context using paths in the build.
Host android.Paths Host android.Paths
@@ -209,7 +212,7 @@ type classLoaderContext struct {
// targetSdkVersion in the manifest or APK is less than that API version. // targetSdkVersion in the manifest or APK is less than that API version.
type classLoaderContextMap map[int]*classLoaderContext 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 { func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
if _, ok := m[sdkVer]; !ok { if _, ok := m[sdkVer]; !ok {
@@ -218,14 +221,19 @@ func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
return m[sdkVer] 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 { func (m classLoaderContextMap) addLibs(ctx android.PathContext, sdkVer int, module *ModuleConfig, libs ...string) bool {
clc := m.getValue(sdkVer) clc := m.getValue(sdkVer)
for _, lib := range libs { for _, lib := range libs {
if p, ok := module.LibraryPaths[lib]; ok && p.Host != nil && p.Device != UnknownInstallLibraryPath { if p, ok := module.LibraryPaths[lib]; ok && p.Host != nil && p.Device != UnknownInstallLibraryPath {
clc.Host = append(clc.Host, p.Host) clc.addLib(lib, p.Host, p.Device)
clc.Target = append(clc.Target, p.Device)
} else { } else {
if sdkVer == anySdkVersion { if sdkVer == AnySdkVersion {
// Fail the build if dexpreopt doesn't know paths to one of the <uses-library> // 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. // 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) android.ReportPathErrorf(ctx, "dexpreopt cannot find path for <uses-library> '%s'", lib)
@@ -239,11 +247,17 @@ func (m classLoaderContextMap) addLibs(ctx android.PathContext, sdkVer int, modu
return true 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) { func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
clc := m.getValue(sdkVer) clc := m.getValue(sdkVer)
for _, lib := range libs { for _, lib := range libs {
clc.Host = append(clc.Host, SystemServerDexJarHostPath(ctx, lib)) clc.addLib(lib, SystemServerDexJarHostPath(ctx, lib), filepath.Join("/system/framework", lib+".jar"))
clc.Target = append(clc.Target, filepath.Join("/system/framework", lib+".jar"))
} }
} }
@@ -261,7 +275,7 @@ func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathC
// as the runtime classpath won't match and the dexpreopted code will be discarded. Therefore in // 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. // 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 // 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 // check that the class loader context provided by the PackageManager agrees with the stored
// class loader context recorded in the .odex file. // class loader context recorded in the .odex file.
@@ -273,21 +287,19 @@ func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module
if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 { if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
// System server jars should be dexpreopted together: class loader context of each jar // System server jars should be dexpreopted together: class loader context of each jar
// should include all preceding jars on the system server classpath. // 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 { } else if module.EnforceUsesLibraries {
// Unconditional class loader context. // Unconditional class loader context.
usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...) usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
if !classLoaderContexts.addLibs(ctx, anySdkVersion, module, usesLibs...) { if !classLoaderContexts.addLibs(ctx, AnySdkVersion, module, usesLibs...) {
return nil return nil
} }
// Conditional class loader context for API version < 28. // Conditional class loader context for API version < 28.
const httpLegacy = "org.apache.http.legacy" const httpLegacy = "org.apache.http.legacy"
if !contains(usesLibs, httpLegacy) { if !classLoaderContexts.addLibs(ctx, 28, module, httpLegacy) {
if !classLoaderContexts.addLibs(ctx, 28, module, httpLegacy) { return nil
return nil
}
} }
// Conditional class loader context for API version < 29. // Conditional class loader context for API version < 29.
@@ -301,10 +313,8 @@ func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module
// Conditional class loader context for API version < 30. // Conditional class loader context for API version < 30.
const testBase = "android.test.base" const testBase = "android.test.base"
if !contains(usesLibs, testBase) { if !classLoaderContexts.addLibs(ctx, 30, module, testBase) {
if !classLoaderContexts.addLibs(ctx, 30, module, testBase) { return nil
return nil
}
} }
} else { } else {
@@ -314,9 +324,32 @@ func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module
// to the &. // to the &.
} }
fixConditionalClassLoaderContext(classLoaderContexts)
return &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, func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
module *ModuleConfig, rule *android.RuleBuilder, archIdx int, classLoaderContexts classLoaderContextMap, module *ModuleConfig, rule *android.RuleBuilder, archIdx int, classLoaderContexts classLoaderContextMap,
profile android.WritablePath, appImage bool, generateDM bool) { profile android.WritablePath, appImage bool, generateDM bool) {
@@ -363,7 +396,7 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g
checkSystemServerOrder(ctx, jarIndex) checkSystemServerOrder(ctx, jarIndex)
clc := classLoaderContexts[anySdkVersion] clc := classLoaderContexts[AnySdkVersion]
rule.Command(). rule.Command().
Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clc.Host.Strings(), ":") + "]"). Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clc.Host.Strings(), ":") + "]").
Implicits(clc.Host). Implicits(clc.Host).
@@ -391,14 +424,15 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g
Text(`eval "$(`).Tool(globalSoong.ConstructContext). Text(`eval "$(`).Tool(globalSoong.ConstructContext).
Text(` --target-sdk-version ${target_sdk_version}`) Text(` --target-sdk-version ${target_sdk_version}`)
for _, ver := range android.SortedIntKeys(classLoaderContexts) { for _, ver := range android.SortedIntKeys(classLoaderContexts) {
clc := classLoaderContexts.getValue(ver) if clc := classLoaderContexts.getValue(ver); len(clc.Host) > 0 {
verString := fmt.Sprintf("%d", ver) verString := fmt.Sprintf("%d", ver)
if ver == anySdkVersion { if ver == AnySdkVersion {
verString = "any" // a special keyword that means any SDK version 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(`)"`) cmd.Text(`)"`)
} else { } else {

View File

@@ -2766,7 +2766,7 @@ func TestUsesLibraries(t *testing.T) {
name: "prebuilt", name: "prebuilt",
apk: "prebuilts/apk/app.apk", apk: "prebuilts/apk/app.apk",
certificate: "platform", certificate: "platform",
uses_libs: ["foo"], uses_libs: ["foo", "android.test.runner"],
optional_uses_libs: [ optional_uses_libs: [
"bar", "bar",
"baz", "baz",
@@ -2804,7 +2804,7 @@ func TestUsesLibraries(t *testing.T) {
cmd = prebuilt.Rule("verify_uses_libraries").RuleParams.Command cmd = prebuilt.Rule("verify_uses_libraries").RuleParams.Command
if w := `uses_library_names="foo"`; !strings.Contains(cmd, w) { if w := `uses_library_names="foo android.test.runner"`; !strings.Contains(cmd, w) {
t.Errorf("wanted %q in %q", w, cmd) t.Errorf("wanted %q in %q", w, cmd)
} }
@@ -2812,20 +2812,48 @@ func TestUsesLibraries(t *testing.T) {
t.Errorf("wanted %q in %q", w, cmd) t.Errorf("wanted %q in %q", w, cmd)
} }
// Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs // Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs.
cmd = app.Rule("dexpreopt").RuleParams.Command cmd = app.Rule("dexpreopt").RuleParams.Command
w := `--target-classpath-for-sdk any` + w := `--target-classpath-for-sdk any` +
` /system/framework/foo.jar` + ` /system/framework/foo.jar` +
`:/system/framework/quuz.jar` + `:/system/framework/quuz.jar` +
`:/system/framework/qux.jar` + `:/system/framework/qux.jar` +
`:/system/framework/runtime-library.jar` + `:/system/framework/runtime-library.jar` +
`:/system/framework/bar.jar` `:/system/framework/bar.jar `
if !strings.Contains(cmd, w) { if !strings.Contains(cmd, w) {
t.Errorf("wanted %q in %q", w, cmd) t.Errorf("wanted %q in %q", w, cmd)
} }
// Test conditional context for target SDK version 28.
if w := `--target-classpath-for-sdk 28` +
` /system/framework/org.apache.http.legacy.jar `; !strings.Contains(cmd, w) {
t.Errorf("wanted %q in %q", w, cmd)
}
// Test conditional context for target SDK version 29.
if w := `--target-classpath-for-sdk 29` +
` /system/framework/android.hidl.base-V1.0-java.jar` +
`:/system/framework/android.hidl.manager-V1.0-java.jar `; !strings.Contains(cmd, w) {
t.Errorf("wanted %q in %q", w, cmd)
}
// Test conditional context for target SDK version 30.
if w := `--target-classpath-for-sdk 30` +
` /system/framework/android.test.base.jar `; !strings.Contains(cmd, w) {
t.Errorf("wanted %q in %q", w, cmd)
}
cmd = prebuilt.Rule("dexpreopt").RuleParams.Command cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
if w := `--target-classpath-for-sdk any /system/framework/foo.jar:/system/framework/bar.jar`; !strings.Contains(cmd, w) { if w := `--target-classpath-for-sdk any` +
` /system/framework/foo.jar` +
`:/system/framework/android.test.runner.jar` +
`:/system/framework/bar.jar `; !strings.Contains(cmd, w) {
t.Errorf("wanted %q in %q", w, cmd)
}
// Test conditional context for target SDK version 30.
if w := `--target-classpath-for-sdk 30` +
` /system/framework/android.test.base.jar `; !strings.Contains(cmd, w) {
t.Errorf("wanted %q in %q", w, cmd) t.Errorf("wanted %q in %q", w, cmd)
} }
} }

View File

@@ -22,6 +22,7 @@ import (
"android/soong/android" "android/soong/android"
"android/soong/cc" "android/soong/cc"
"android/soong/dexpreopt"
"android/soong/python" "android/soong/python"
"github.com/google/blueprint" "github.com/google/blueprint"
@@ -152,6 +153,24 @@ func GatherRequiredDepsForTest() string {
`, extra) `, extra)
} }
// For class loader context and <uses-library> tests.
dexpreoptModules := []string{"android.test.runner"}
dexpreoptModules = append(dexpreoptModules, dexpreopt.CompatUsesLibs...)
dexpreoptModules = append(dexpreoptModules, dexpreopt.OptionalCompatUsesLibs...)
for _, extra := range dexpreoptModules {
bp += fmt.Sprintf(`
java_library {
name: "%s",
srcs: ["a.java"],
sdk_version: "none",
system_modules: "stable-core-platform-api-stubs-system-modules",
compile_dex: true,
installable: true,
}
`, extra)
}
bp += ` bp += `
java_library { java_library {
name: "framework", name: "framework",
@@ -166,48 +185,7 @@ func GatherRequiredDepsForTest() string {
android_app { android_app {
name: "framework-res", name: "framework-res",
sdk_version: "core_platform", sdk_version: "core_platform",
} }`
java_library {
name: "android.hidl.base-V1.0-java",
srcs: ["a.java"],
sdk_version: "none",
system_modules: "stable-core-platform-api-stubs-system-modules",
installable: true,
}
java_library {
name: "android.hidl.manager-V1.0-java",
srcs: ["a.java"],
sdk_version: "none",
system_modules: "stable-core-platform-api-stubs-system-modules",
installable: true,
}
java_library {
name: "org.apache.http.legacy",
srcs: ["a.java"],
sdk_version: "none",
system_modules: "stable-core-platform-api-stubs-system-modules",
installable: true,
}
java_library {
name: "android.test.base",
srcs: ["a.java"],
sdk_version: "none",
system_modules: "stable-core-platform-api-stubs-system-modules",
installable: true,
}
java_library {
name: "android.test.mock",
srcs: ["a.java"],
sdk_version: "none",
system_modules: "stable-core-platform-api-stubs-system-modules",
installable: true,
}
`
systemModules := []string{ systemModules := []string{
"core-current-stubs-system-modules", "core-current-stubs-system-modules",