Add support for excluding libraries from class loader contexts
A number of tests in the cts/tests/signature/api-check check for the accessibility of classes from the android.test.base, android.test.runner and android.test.mock libraries. Some tests expect to find the classes other do not. Unfortunately, the tests use libraries, specifically compatibility-device-util-axt, that depend on the android.test... libraries which causes Soong to implicitly add <uses-library> entries to the manifest so that they will be accessible at runtime. That causes the tests that do not expect to find the classes to fail. Bug: 209607558 Test: m nothing Change-Id: I54c194ab23d5a70df790ece3fe98f2b3d6a1c1f6
This commit is contained in:
@@ -210,6 +210,34 @@ type ClassLoaderContext struct {
|
||||
Subcontexts []*ClassLoaderContext
|
||||
}
|
||||
|
||||
// excludeLibs excludes the libraries from this ClassLoaderContext.
|
||||
//
|
||||
// This treats the supplied context as being immutable (as it may come from a dependency). So, it
|
||||
// implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
|
||||
// within this context then this will return a deep copy of this without those libraries.
|
||||
//
|
||||
// If this ClassLoaderContext matches one of the libraries to exclude then this returns (nil, true)
|
||||
// to indicate that this context should be excluded from the containing list.
|
||||
//
|
||||
// If any of this ClassLoaderContext's Subcontexts reference the excluded libraries then this
|
||||
// returns a pointer to a copy of this without the excluded libraries and true to indicate that this
|
||||
// was copied.
|
||||
//
|
||||
// Otherwise, this returns a pointer to this and false to indicate that this was not copied.
|
||||
func (c *ClassLoaderContext) excludeLibs(excludedLibs []string) (*ClassLoaderContext, bool) {
|
||||
if android.InList(c.Name, excludedLibs) {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
if excludedList, modified := excludeLibsFromCLCList(c.Subcontexts, excludedLibs); modified {
|
||||
clcCopy := *c
|
||||
clcCopy.Subcontexts = excludedList
|
||||
return &clcCopy, true
|
||||
}
|
||||
|
||||
return c, false
|
||||
}
|
||||
|
||||
// ClassLoaderContextMap is a map from SDK version to CLC. There is a special entry with key
|
||||
// AnySdkVersion that stores unconditional CLC that is added regardless of the target SDK version.
|
||||
//
|
||||
@@ -408,6 +436,67 @@ func (clcMap ClassLoaderContextMap) Dump() string {
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
// excludeLibsFromCLCList excludes the libraries from the ClassLoaderContext in this list.
|
||||
//
|
||||
// This treats the supplied list as being immutable (as it may come from a dependency). So, it
|
||||
// implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
|
||||
// within the contexts in the list then this will return a deep copy of the list without those
|
||||
// libraries.
|
||||
//
|
||||
// If any of the ClassLoaderContext in the list reference the excluded libraries then this returns a
|
||||
// copy of this list without the excluded libraries and true to indicate that this was copied.
|
||||
//
|
||||
// Otherwise, this returns the list and false to indicate that this was not copied.
|
||||
func excludeLibsFromCLCList(clcList []*ClassLoaderContext, excludedLibs []string) ([]*ClassLoaderContext, bool) {
|
||||
modifiedList := false
|
||||
copiedList := make([]*ClassLoaderContext, 0, len(clcList))
|
||||
for _, clc := range clcList {
|
||||
resultClc, modifiedClc := clc.excludeLibs(excludedLibs)
|
||||
if resultClc != nil {
|
||||
copiedList = append(copiedList, resultClc)
|
||||
}
|
||||
modifiedList = modifiedList || modifiedClc
|
||||
}
|
||||
|
||||
if modifiedList {
|
||||
return copiedList, true
|
||||
} else {
|
||||
return clcList, false
|
||||
}
|
||||
}
|
||||
|
||||
// ExcludeLibs excludes the libraries from the ClassLoaderContextMap.
|
||||
//
|
||||
// If the list o libraries is empty then this returns the ClassLoaderContextMap.
|
||||
//
|
||||
// This treats the ClassLoaderContextMap as being immutable (as it may come from a dependency). So,
|
||||
// it implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
|
||||
// within the contexts in the map then this will return a deep copy of the map without those
|
||||
// libraries.
|
||||
//
|
||||
// Otherwise, this returns the map unchanged.
|
||||
func (clcMap ClassLoaderContextMap) ExcludeLibs(excludedLibs []string) ClassLoaderContextMap {
|
||||
if len(excludedLibs) == 0 {
|
||||
return clcMap
|
||||
}
|
||||
|
||||
excludedClcMap := make(ClassLoaderContextMap)
|
||||
modifiedMap := false
|
||||
for sdkVersion, clcList := range clcMap {
|
||||
excludedList, modifiedList := excludeLibsFromCLCList(clcList, excludedLibs)
|
||||
if len(excludedList) != 0 {
|
||||
excludedClcMap[sdkVersion] = excludedList
|
||||
}
|
||||
modifiedMap = modifiedMap || modifiedList
|
||||
}
|
||||
|
||||
if modifiedMap {
|
||||
return excludedClcMap
|
||||
} else {
|
||||
return clcMap
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@@ -284,6 +284,111 @@ func TestCLCSdkVersionOrder(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestCLCMExcludeLibs(t *testing.T) {
|
||||
ctx := testContext()
|
||||
const optional = false
|
||||
const implicit = true
|
||||
|
||||
excludeLibs := func(t *testing.T, m ClassLoaderContextMap, excluded_libs ...string) ClassLoaderContextMap {
|
||||
// Dump the CLCM before creating a new copy that excludes a specific set of libraries.
|
||||
before := m.Dump()
|
||||
|
||||
// Create a new CLCM that excludes some libraries.
|
||||
c := m.ExcludeLibs(excluded_libs)
|
||||
|
||||
// Make sure that the original CLCM was not changed.
|
||||
after := m.Dump()
|
||||
android.AssertStringEquals(t, "input CLCM modified", before, after)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
t.Run("exclude nothing", func(t *testing.T) {
|
||||
m := make(ClassLoaderContextMap)
|
||||
m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
|
||||
|
||||
a := excludeLibs(t, m)
|
||||
|
||||
android.AssertStringEquals(t, "output CLCM ", `{
|
||||
"28": [
|
||||
{
|
||||
"Name": "a",
|
||||
"Optional": false,
|
||||
"Implicit": true,
|
||||
"Host": "out/soong/a.jar",
|
||||
"Device": "/system/a.jar",
|
||||
"Subcontexts": []
|
||||
}
|
||||
]
|
||||
}`, a.Dump())
|
||||
})
|
||||
|
||||
t.Run("one item from list", func(t *testing.T) {
|
||||
m := make(ClassLoaderContextMap)
|
||||
m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
|
||||
m.AddContext(ctx, 28, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
|
||||
|
||||
a := excludeLibs(t, m, "a")
|
||||
|
||||
expected := `{
|
||||
"28": [
|
||||
{
|
||||
"Name": "b",
|
||||
"Optional": false,
|
||||
"Implicit": true,
|
||||
"Host": "out/soong/b.jar",
|
||||
"Device": "/system/b.jar",
|
||||
"Subcontexts": []
|
||||
}
|
||||
]
|
||||
}`
|
||||
android.AssertStringEquals(t, "output CLCM ", expected, a.Dump())
|
||||
})
|
||||
|
||||
t.Run("all items from a list", func(t *testing.T) {
|
||||
m := make(ClassLoaderContextMap)
|
||||
m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
|
||||
m.AddContext(ctx, 28, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
|
||||
|
||||
a := excludeLibs(t, m, "a", "b")
|
||||
|
||||
android.AssertStringEquals(t, "output CLCM ", `{}`, a.Dump())
|
||||
})
|
||||
|
||||
t.Run("items from a subcontext", func(t *testing.T) {
|
||||
s := make(ClassLoaderContextMap)
|
||||
s.AddContext(ctx, AnySdkVersion, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
|
||||
s.AddContext(ctx, AnySdkVersion, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
|
||||
|
||||
m := make(ClassLoaderContextMap)
|
||||
m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), s)
|
||||
|
||||
a := excludeLibs(t, m, "b")
|
||||
|
||||
android.AssertStringEquals(t, "output CLCM ", `{
|
||||
"28": [
|
||||
{
|
||||
"Name": "a",
|
||||
"Optional": false,
|
||||
"Implicit": true,
|
||||
"Host": "out/soong/a.jar",
|
||||
"Device": "/system/a.jar",
|
||||
"Subcontexts": [
|
||||
{
|
||||
"Name": "c",
|
||||
"Optional": false,
|
||||
"Implicit": true,
|
||||
"Host": "out/soong/c.jar",
|
||||
"Device": "/system/c.jar",
|
||||
"Subcontexts": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`, a.Dump())
|
||||
})
|
||||
}
|
||||
|
||||
func checkError(t *testing.T, have error, want string) {
|
||||
if have == nil {
|
||||
t.Errorf("\nwant error: '%s'\nhave: none", want)
|
||||
|
Reference in New Issue
Block a user