From 8cbc5d269b20b5743679bfb8684ed174dcf58a30 Mon Sep 17 00:00:00 2001 From: Ulya Trafimovich Date: Tue, 3 Nov 2020 15:15:46 +0000 Subject: [PATCH] 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 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 --- dexpreopt/class_loader_context.go | 415 ++++++++++++------------- dexpreopt/class_loader_context_test.go | 133 ++++---- dexpreopt/config.go | 10 +- dexpreopt/dexpreopt.go | 34 +- dexpreopt/dexpreopt_test.go | 4 +- java/aar.go | 14 +- java/android_manifest.go | 7 +- java/androidmk.go | 2 +- java/app.go | 29 +- java/app_test.go | 8 +- java/device_host_converter.go | 2 +- java/dexpreopt.go | 14 +- java/java.go | 36 ++- java/java_test.go | 4 +- 14 files changed, 351 insertions(+), 361 deletions(-) diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go index 6d778123f..8d6190190 100644 --- a/dexpreopt/class_loader_context.go +++ b/dexpreopt/class_loader_context.go @@ -16,7 +16,7 @@ package dexpreopt import ( "fmt" - "path/filepath" + "strconv" "strings" "android/soong/android" @@ -49,39 +49,39 @@ var CompatUsesLibs = android.CopyOf(CompatUsesLibs29) const UnknownInstallLibraryPath = "error" -const AnySdkVersion int = 9999 // should go last in class loader context +// AnySdkVersion means that the class loader context is needed regardless of the targetSdkVersion +// of the app. The numeric value affects the key order in the map and, as a result, the order of +// arguments passed to construct_context.py (high value means that the unconditional context goes +// last). We use the converntional "current" SDK level (10000), but any big number would do as well. +const AnySdkVersion int = android.FutureApiLevelInt -// LibraryPath contains paths to the library DEX jar on host and on device. -type LibraryPath struct { - Host android.Path +// ClassLoaderContext is a tree of libraries used by the dexpreopted module with their dependencies. +// The context is used by dex2oat to compile the module and recorded in the AOT-compiled files, so +// that it can be checked agains the run-time class loader context on device. If there is a mismatch +// at runtime, AOT-compiled code is rejected. +type ClassLoaderContext struct { + // The name of the library (same as the name of the module that contains it). + Name string + + // On-host build path to the library dex file (used in dex2oat argument --class-loader-context). + Host android.Path + + // On-device install path (used in dex2oat argument --stored-class-loader-context). Device string + + // Nested class loader subcontexts for dependencies. + Subcontexts []*ClassLoaderContext } -// LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar. -type LibraryPaths map[string]*LibraryPath +// ClassLoaderContextMap is a map from SDK version to a class loader context. +// There is a special entry with key AnySdkVersion that stores unconditional class loader context. +// Other entries store conditional contexts that should be added for some apps that have +// targetSdkVersion in the manifest lower than the key SDK version. +type ClassLoaderContextMap map[int][]*ClassLoaderContext -type classLoaderContext struct { - // Library names - Names []string - - // The class loader context using paths in the build. - Host android.Paths - - // The class loader context using paths as they will be on the device. - Target []string -} - -// A map of class loader contexts for each SDK version. -// A map entry for "any" version contains libraries that are unconditionally added to class loader -// context. Map entries for existing versions contains libraries that were in the default classpath -// until that API version, and should be added to class loader context if and only if the -// targetSdkVersion in the manifest or APK is less than that API version. -type classLoaderContextMap map[int]*classLoaderContext - -// Add a new library path to the map, unless a path for this library already exists. -// If necessary, check that the build and install paths exist. -func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleInstallPathContext, lib string, - hostPath, installPath android.Path, strict bool) error { +// Add class loader context for the given library to the map entry for the given SDK version. +func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string, + hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error { // If missing dependencies are allowed, the build shouldn't fail when a is // not found. However, this is likely to result is disabling dexpreopt, as it won't be @@ -89,263 +89,254 @@ func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleInstallPathContext strict = strict && !ctx.Config().AllowMissingDependencies() if hostPath == nil && strict { - return fmt.Errorf("unknown build path to '%s'", lib) + return fmt.Errorf("unknown build path to \"%s\"", lib) } + devicePath := UnknownInstallLibraryPath if installPath == nil { if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) { // Assume that compatibility libraries are installed in /system/framework. installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar") } else if strict { - return fmt.Errorf("unknown install path to '%s'", lib) - } - } - - // Add a library only if the build and install path to it is known. - if _, present := libPaths[lib]; !present { - var devicePath string - if installPath != nil { - devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath)) + return fmt.Errorf("unknown install path to \"%s\"", lib) } else { // For some stub libraries the only known thing is the name of their implementation // library, but the library itself is unavailable (missing or part of a prebuilt). In // such cases we still need to add the library to tags in the manifest, - // but we cannot use if for dexpreopt. - devicePath = UnknownInstallLibraryPath + // but we cannot use it for dexpreopt. } - libPaths[lib] = &LibraryPath{hostPath, devicePath} } + if installPath != nil { + devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath)) + } + + subcontexts := nestedClcMap[AnySdkVersion] + + // If the library with this name is already present as one of the unconditional top-level + // components, do not re-add it. + for _, clc := range clcMap[sdkVer] { + if clc.Name == lib { + return nil + } + } + + clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{ + Name: lib, + Host: hostPath, + Device: devicePath, + Subcontexts: subcontexts, + }) return nil } -// Wrapper around addLibraryPath that does error reporting. -func (libPaths LibraryPaths) addLibraryPathOrReportError(ctx android.ModuleInstallPathContext, lib string, - hostPath, installPath android.Path, strict bool) { +// Wrapper around addContext that reports errors. +func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string, + hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) { - err := libPaths.addLibraryPath(ctx, lib, hostPath, installPath, strict) + err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap) if err != nil { + ctx.ModuleErrorf(err.Error()) android.ReportPathErrorf(ctx, err.Error()) } } -// Add a new library path to the map. Enforce checks that the library paths exist. -func (libPaths LibraryPaths) AddLibraryPath(ctx android.ModuleInstallPathContext, lib string, hostPath, installPath android.Path) { - libPaths.addLibraryPathOrReportError(ctx, lib, hostPath, installPath, true) +// Add class loader context. Fail on unknown build/install paths. +func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string, + hostPath, installPath android.Path) { + + clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil) } -// Add a new library path to the map, if the library exists (name is not nil). -// Don't enforce checks that the library paths exist. Some libraries may be missing from the build, -// but their names still need to be added to tags in the manifest. -func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.ModuleInstallPathContext, lib *string, hostPath, installPath android.Path) { +// Add class loader context if the library exists. Don't fail on unknown build/install paths. +func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string, + hostPath, installPath android.Path) { + if lib != nil { - libPaths.addLibraryPathOrReportError(ctx, *lib, hostPath, installPath, false) + clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil) } } -// Add library paths from the second map to the first map (do not override existing entries). -func (libPaths LibraryPaths) AddLibraryPaths(otherPaths LibraryPaths) { - for lib, path := range otherPaths { - if _, present := libPaths[lib]; !present { - libPaths[lib] = path - } - } +// Add class loader context for the given SDK version. Fail on unknown build/install paths. +func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int, + lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) { + + clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, true, nestedClcMap) } -func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext { - if _, ok := m[sdkVer]; !ok { - m[sdkVer] = &classLoaderContext{} - } - 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, error) { - - clc := m.getValue(sdkVer) - for _, lib := range libs { - if p, ok := module.LibraryPaths[lib]; ok && p.Host != nil && p.Device != UnknownInstallLibraryPath { - clc.addLib(lib, p.Host, p.Device) - } else { - if sdkVer == AnySdkVersion { - // Fail the build if dexpreopt doesn't know paths to one of the - // dependencies. In the future we may need to relax this and just disable dexpreopt. - return false, fmt.Errorf("dexpreopt cannot find path for '%s'", lib) - } else { - // No error for compatibility libraries, as Soong doesn't know if they are needed - // (this depends on the targetSdkVersion in the manifest). - return false, nil +// Merge the other class loader context map into this one, do not override existing entries. +func (clcMap ClassLoaderContextMap) AddContextMap(otherClcMap ClassLoaderContextMap) { + for sdkVer, otherClcs := range otherClcMap { + for _, otherClc := range otherClcs { + alreadyHave := false + for _, clc := range clcMap[sdkVer] { + if clc.Name == otherClc.Name { + alreadyHave = true + break + } + } + if !alreadyHave { + clcMap[sdkVer] = append(clcMap[sdkVer], otherClc) } } } - return true, nil } -func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) { - clc := m.getValue(sdkVer) - for _, lib := range libs { - clc.addLib(lib, SystemServerDexJarHostPath(ctx, lib), filepath.Join("/system/framework", lib+".jar")) +// List of libraries in the unconditional class loader context, excluding dependencies of shared libraries. +func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) { + if clcMap != nil { + // compatibility libraries (those in conditional context) are not added to tags + ulibs = usesLibsRec(clcMap[AnySdkVersion]) + ulibs = android.FirstUniqueStrings(ulibs) } + return ulibs } -func (m classLoaderContextMap) usesLibs() []string { - if clc, ok := m[AnySdkVersion]; ok { - return clc.Names +func usesLibsRec(clcs []*ClassLoaderContext) (ulibs []string) { + for _, clc := range clcs { + ulibs = append(ulibs, clc.Name) + ulibs = append(ulibs, usesLibsRec(clc.Subcontexts)...) } - return nil -} - -// genClassLoaderContext generates host and target class loader context to be passed to the dex2oat -// command for the dexpreopted module. There are three possible cases: -// -// 1. System server jars. They have a special class loader context that includes other system -// server jars. -// -// 2. Library jars or APKs which have precise list of their libs. Their class loader -// context includes build and on-device paths to these libs. In some cases it may happen that -// the path to a is unknown (e.g. the dexpreopted module may depend on stubs -// library, whose implementation library is missing from the build altogether). In such case -// dexpreopting with the is impossible, and dexpreopting without it is pointless, -// 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. -// -// 3. All other library jars or APKs for which the exact 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. -// -func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) (*classLoaderContextMap, error) { - classLoaderContexts := make(classLoaderContextMap) - systemServerJars := NonUpdatableSystemServerJars(ctx, global) - - 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]...) - - } else if module.EnforceUsesLibraries { - // Unconditional class loader context. - usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...) - if ok, err := classLoaderContexts.addLibs(ctx, AnySdkVersion, module, usesLibs...); !ok { - return nil, err - } - - // Conditional class loader context for API version < 28. - const httpLegacy = "org.apache.http.legacy" - if ok, err := classLoaderContexts.addLibs(ctx, 28, module, httpLegacy); !ok { - return nil, err - } - - // Conditional class loader context for API version < 29. - usesLibs29 := []string{ - "android.hidl.base-V1.0-java", - "android.hidl.manager-V1.0-java", - } - if ok, err := classLoaderContexts.addLibs(ctx, 29, module, usesLibs29...); !ok { - return nil, err - } - - // Conditional class loader context for API version < 30. - if ok, err := classLoaderContexts.addLibs(ctx, 30, module, OptionalCompatUsesLibs30...); !ok { - return nil, err - } - - } 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 &. - } - - fixConditionalClassLoaderContext(classLoaderContexts) - - return &classLoaderContexts, nil + return ulibs } // 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. // -// TODO(b/132357300): -// - remove android.hidl.manager and android.hidl.base unless the app is a system app. +// TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps. // -func fixConditionalClassLoaderContext(clcMap classLoaderContextMap) { - usesLibs := clcMap.usesLibs() +func fixClassLoaderContext(clcMap ClassLoaderContextMap) { + usesLibs := clcMap.UsesLibs() - for sdkVer, clc := range clcMap { + for sdkVer, clcs := range clcMap { if sdkVer == AnySdkVersion { continue } - clcMap[sdkVer] = &classLoaderContext{} - for i, lib := range clc.Names { - if android.InList(lib, usesLibs) { + fixedClcs := []*ClassLoaderContext{} + for _, clc := range clcs { + if android.InList(clc.Name, usesLibs) { // skip compatibility libraries that are already included in unconditional context - } else if lib == AndroidTestMock && !android.InList("android.test.runner", usesLibs) { + } else if clc.Name == AndroidTestMock && !android.InList("android.test.runner", usesLibs) { // android.test.mock is only needed as a compatibility library (in conditional class // loader context) if android.test.runner is used, otherwise skip it } else { - clcMap[sdkVer].addLib(lib, clc.Host[i], clc.Target[i]) + fixedClcs = append(fixedClcs, clc) } + clcMap[sdkVer] = fixedClcs } } } -// Return the class loader context as a string and a slice of build paths for all dependencies. -func computeClassLoaderContext(ctx android.PathContext, clcMap classLoaderContextMap) (clcStr string, paths android.Paths) { - for _, ver := range android.SortedIntKeys(clcMap) { - clc := clcMap.getValue(ver) - - clcLen := len(clc.Names) - if clcLen != len(clc.Host) || clcLen != len(clc.Target) { - android.ReportPathErrorf(ctx, "ill-formed class loader context") +// Return true if all build/install library paths are valid (including recursive subcontexts), +// otherwise return false. A build path is valid if it's not nil. An install path is valid if it's +// not equal to a special "error" value. +func validateClassLoaderContext(clcMap ClassLoaderContextMap) (bool, error) { + for sdkVer, clcs := range clcMap { + if valid, err := validateClassLoaderContextRec(sdkVer, clcs); !valid || err != nil { + return valid, err } + } + return true, nil +} - var hostClc, targetClc []string - var hostPaths android.Paths - - for i := 0; i < clcLen; i++ { - hostStr := "PCL[" + clc.Host[i].String() + "]" - targetStr := "PCL[" + clc.Target[i] + "]" - - hostClc = append(hostClc, hostStr) - targetClc = append(targetClc, targetStr) - hostPaths = append(hostPaths, clc.Host[i]) +func validateClassLoaderContextRec(sdkVer int, clcs []*ClassLoaderContext) (bool, error) { + for _, clc := range clcs { + if clc.Host == nil || clc.Device == UnknownInstallLibraryPath { + if sdkVer == AnySdkVersion { + // Return error if dexpreopt doesn't know paths to one of the + // dependencies. In the future we may need to relax this and just disable dexpreopt. + return false, fmt.Errorf("invalid path for \"%s\"", clc.Name) + } else { + // No error for compatibility libraries, as Soong doesn't know if they are needed + // (this depends on the targetSdkVersion in the manifest), but the CLC is invalid. + return false, nil + } } + if valid, err := validateClassLoaderContextRec(sdkVer, clc.Subcontexts); !valid || err != nil { + return valid, err + } + } + return true, nil +} +// Return the class loader context as a string, and a slice of build paths for all dependencies. +// Perform a depth-first preorder traversal of the class loader context tree for each SDK version. +// Return the resulting string and a slice of on-host build paths to all library dependencies. +func ComputeClassLoaderContext(clcMap ClassLoaderContextMap) (clcStr string, paths android.Paths) { + for _, sdkVer := range android.SortedIntKeys(clcMap) { // determinisitc traversal order + sdkVerStr := fmt.Sprintf("%d", sdkVer) + if sdkVer == AnySdkVersion { + sdkVerStr = "any" // a special keyword that means any SDK version + } + hostClc, targetClc, hostPaths := computeClassLoaderContextRec(clcMap[sdkVer]) if hostPaths != nil { - sdkVerStr := fmt.Sprintf("%d", ver) - if ver == AnySdkVersion { - sdkVerStr = "any" // a special keyword that means any SDK version - } - clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, strings.Join(hostClc, "#")) - clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, strings.Join(targetClc, "#")) - paths = append(paths, hostPaths...) + clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, hostClc) + clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, targetClc) } + paths = append(paths, hostPaths...) } - - return clcStr, paths + return clcStr, android.FirstUniquePaths(paths) } +func computeClassLoaderContextRec(clcs []*ClassLoaderContext) (string, string, android.Paths) { + var paths android.Paths + var clcsHost, clcsTarget []string + + for _, clc := range clcs { + subClcHost, subClcTarget, subPaths := computeClassLoaderContextRec(clc.Subcontexts) + if subPaths != nil { + subClcHost = "{" + subClcHost + "}" + subClcTarget = "{" + subClcTarget + "}" + } + + clcsHost = append(clcsHost, "PCL["+clc.Host.String()+"]"+subClcHost) + clcsTarget = append(clcsTarget, "PCL["+clc.Device+"]"+subClcTarget) + + paths = append(paths, clc.Host) + paths = append(paths, subPaths...) + } + + clcHost := strings.Join(clcsHost, "#") + clcTarget := strings.Join(clcsTarget, "#") + + return clcHost, clcTarget, paths +} + +// Paths to a on host and on device. type jsonLibraryPath struct { Host string Device string } -type jsonLibraryPaths map[string]jsonLibraryPath +// Class loader contexts that come from Make (via JSON dexpreopt.config) files have simpler +// structure than Soong class loader contexts: they are flat maps from a name to its +// on-host and on-device paths. There are no nested subcontexts. It is a limitation of the current +// Make implementation. +type jsonClassLoaderContext map[string]jsonLibraryPath -// convert JSON map of library paths to LibraryPaths -func constructLibraryPaths(ctx android.PathContext, paths jsonLibraryPaths) LibraryPaths { - m := LibraryPaths{} - for lib, path := range paths { - m[lib] = &LibraryPath{ - constructPath(ctx, path.Host), - path.Device, +// A map from SDK version (represented with a JSON string) to JSON class loader context. +type jsonClassLoaderContextMap map[string]jsonClassLoaderContext + +// Convert JSON class loader context map to ClassLoaderContextMap. +func fromJsonClassLoaderContext(ctx android.PathContext, jClcMap jsonClassLoaderContextMap) ClassLoaderContextMap { + clcMap := make(ClassLoaderContextMap) + for sdkVerStr, clc := range jClcMap { + sdkVer, ok := strconv.Atoi(sdkVerStr) + if ok != nil { + if sdkVerStr == "any" { + sdkVer = AnySdkVersion + } else { + android.ReportPathErrorf(ctx, "failed to parse SDK version in dexpreopt.config: '%s'", sdkVerStr) + } + } + for lib, path := range clc { + clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{ + Name: lib, + Host: constructPath(ctx, path.Host), + Device: path.Device, + Subcontexts: nil, + }) } } - return m + return clcMap } diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go index 51c1a0a1f..269a0dbc0 100644 --- a/dexpreopt/class_loader_context_test.go +++ b/dexpreopt/class_loader_context_test.go @@ -37,91 +37,74 @@ func TestCLC(t *testing.T) { // ├── b // ├── c // ├── d - // ├── a2 - // ├── b2 - // ├── c2 - // ├── a1 - // ├── b1 + // │   ├── a2 + // │   ├── b2 + // │   └── c2 + // │   ├── a1 + // │   └── b1 // ├── f // ├── a3 // └── b3 // ctx := testContext() - lp := make(LibraryPaths) + m := make(ClassLoaderContextMap) - lp.AddLibraryPath(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a")) - lp.AddLibraryPath(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b")) + m.AddContext(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a")) + m.AddContext(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b")) // "Maybe" variant in the good case: add as usual. c := "c" - lp.MaybeAddLibraryPath(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c")) + m.MaybeAddContext(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c")) // "Maybe" variant in the bad case: don't add library with unknown name, keep going. - lp.MaybeAddLibraryPath(ctx, nil, nil, nil) + m.MaybeAddContext(ctx, nil, nil, nil) // Add some libraries with nested subcontexts. - lp1 := make(LibraryPaths) - lp1.AddLibraryPath(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1")) - lp1.AddLibraryPath(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1")) + m1 := make(ClassLoaderContextMap) + m1.AddContext(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1")) + m1.AddContext(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1")) - lp2 := make(LibraryPaths) - lp2.AddLibraryPath(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2")) - lp2.AddLibraryPath(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2")) - lp2.AddLibraryPath(ctx, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2")) - lp2.AddLibraryPaths(lp1) + m2 := make(ClassLoaderContextMap) + m2.AddContext(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2")) + m2.AddContext(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2")) + m2.AddContextForSdk(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1) - lp.AddLibraryPath(ctx, "d", buildPath(ctx, "d"), installPath(ctx, "d")) - lp.AddLibraryPaths(lp2) + m3 := make(ClassLoaderContextMap) + m3.AddContext(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3")) + m3.AddContext(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3")) - lp3 := make(LibraryPaths) - lp3.AddLibraryPath(ctx, "f", buildPath(ctx, "f"), installPath(ctx, "f")) - lp3.AddLibraryPath(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3")) - lp3.AddLibraryPath(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3")) - lp.AddLibraryPaths(lp3) + m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2) + // When the same library is both in conditional and unconditional context, it should be removed + // from conditional context. + m.AddContextForSdk(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil) + m.AddContextForSdk(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil) + m.AddContextMap(m3) // Compatibility libraries with unknown install paths get default paths. - lp.AddLibraryPath(ctx, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil) - lp.AddLibraryPath(ctx, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil) - lp.AddLibraryPath(ctx, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil) - - module := testSystemModuleConfig(ctx, "test") - module.LibraryPaths = lp - - m := make(classLoaderContextMap) - valid := true - - ok, err := m.addLibs(ctx, AnySdkVersion, module, "a", "b", "c", "d", "a2", "b2", "c2", "a1", "b1", "f", "a3", "b3") - valid = valid && ok && err == nil - - // Add compatibility libraries to conditional CLC for SDK level 29. - ok, err = m.addLibs(ctx, 29, module, AndroidHidlManager, AndroidHidlBase) - valid = valid && ok && err == nil + m.AddContextForSdk(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil) + m.AddContextForSdk(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil) // Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only // needed as a compatibility library if "android.test.runner" is in CLC as well. - ok, err = m.addLibs(ctx, 30, module, AndroidTestMock) - valid = valid && ok && err == nil + m.AddContextForSdk(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil) - // When the same library is both in conditional and unconditional context, it should be removed - // from conditional context. - ok, err = m.addLibs(ctx, 42, module, "f") - valid = valid && ok && err == nil + valid, validationError := validateClassLoaderContext(m) - fixConditionalClassLoaderContext(m) + fixClassLoaderContext(m) var haveStr string var havePaths android.Paths var haveUsesLibs []string - if valid { - haveStr, havePaths = computeClassLoaderContext(ctx, m) - haveUsesLibs = m.usesLibs() + if valid && validationError == nil { + haveStr, havePaths = ComputeClassLoaderContext(m) + haveUsesLibs = m.UsesLibs() } // Test that validation is successful (all paths are known). t.Run("validate", func(t *testing.T) { - if !valid { + if !(valid && validationError == nil) { t.Errorf("invalid class loader context") } }) @@ -135,14 +118,14 @@ func TestCLC(t *testing.T) { "PCL[/system/framework/" + AndroidHidlManager + ".jar]#" + "PCL[/system/framework/" + AndroidHidlBase + ".jar]" + " --host-context-for-sdk any " + - "PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]#" + - "PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]#" + - "PCL[out/a1.jar]#PCL[out/b1.jar]#" + + "PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]" + + "{PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]" + + "{PCL[out/a1.jar]#PCL[out/b1.jar]}}#" + "PCL[out/f.jar]#PCL[out/a3.jar]#PCL[out/b3.jar]" + " --target-context-for-sdk any " + - "PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]#" + - "PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]#" + - "PCL[/system/a1.jar]#PCL[/system/b1.jar]#" + + "PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]" + + "{PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]" + + "{PCL[/system/a1.jar]#PCL[/system/b1.jar]}}#" + "PCL[/system/f.jar]#PCL[/system/a3.jar]#PCL[/system/b3.jar]" if wantStr != haveStr { t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr) @@ -175,32 +158,40 @@ func TestCLC(t *testing.T) { // Test that an unexpected unknown build path causes immediate error. func TestCLCUnknownBuildPath(t *testing.T) { ctx := testContext() - lp := make(LibraryPaths) - err := lp.addLibraryPath(ctx, "a", nil, nil, true) - checkError(t, err, "unknown build path to 'a'") + m := make(ClassLoaderContextMap) + err := m.addContext(ctx, AnySdkVersion, "a", nil, nil, true, nil) + checkError(t, err, "unknown build path to \"a\"") } // Test that an unexpected unknown install path causes immediate error. func TestCLCUnknownInstallPath(t *testing.T) { ctx := testContext() - lp := make(LibraryPaths) - err := lp.addLibraryPath(ctx, "a", buildPath(ctx, "a"), nil, true) - checkError(t, err, "unknown install path to 'a'") + m := make(ClassLoaderContextMap) + err := m.addContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, true, nil) + checkError(t, err, "unknown install path to \"a\"") } func TestCLCMaybeAdd(t *testing.T) { ctx := testContext() - lp := make(LibraryPaths) + m := make(ClassLoaderContextMap) a := "a" - lp.MaybeAddLibraryPath(ctx, &a, nil, nil) + m.MaybeAddContext(ctx, &a, nil, nil) - module := testSystemModuleConfig(ctx, "test") - module.LibraryPaths = lp + // The library should be added to tags by the manifest_fixer. + t.Run("maybe add", func(t *testing.T) { + haveUsesLibs := m.UsesLibs() + wantUsesLibs := []string{"a"} + if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) { + t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs) + } + }) - m := make(classLoaderContextMap) - _, err := m.addLibs(ctx, AnySdkVersion, module, "a") - checkError(t, err, "dexpreopt cannot find path for 'a'") + // But class loader context in such cases should raise an error on validation. + t.Run("validate", func(t *testing.T) { + _, err := validateClassLoaderContext(m) + checkError(t, err, "invalid path for \"a\"") + }) } func checkError(t *testing.T, have error, want string) { diff --git a/dexpreopt/config.go b/dexpreopt/config.go index 03accc8e1..cfa4c5590 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -114,10 +114,8 @@ type ModuleConfig struct { ProfileIsTextListing bool ProfileBootListing android.OptionalPath - EnforceUsesLibraries bool - OptionalUsesLibraries []string - UsesLibraries []string - LibraryPaths LibraryPaths + EnforceUsesLibraries bool + ClassLoaderContexts ClassLoaderContextMap Archs []android.ArchType DexPreoptImages []android.Path @@ -265,7 +263,7 @@ func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, err DexPath string ManifestPath string ProfileClassListing string - LibraryPaths jsonLibraryPaths + ClassLoaderContexts jsonClassLoaderContextMap DexPreoptImages []string DexPreoptImageLocations []string PreoptBootClassPathDexFiles []string @@ -283,7 +281,7 @@ func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, err config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath) config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath) config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing)) - config.ModuleConfig.LibraryPaths = constructLibraryPaths(ctx, config.LibraryPaths) + config.ModuleConfig.ClassLoaderContexts = fromJsonClassLoaderContext(ctx, config.ClassLoaderContexts) config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages) config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles) diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go index a07f1fa2b..65380feae 100644 --- a/dexpreopt/dexpreopt.go +++ b/dexpreopt/dexpreopt.go @@ -81,16 +81,18 @@ func GenerateDexpreoptRule(ctx android.PathContext, globalSoong *GlobalSoongConf } 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 bootProfileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, } 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 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g 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 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g 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 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g } // 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 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 diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go index ec3154903..feabd7004 100644 --- a/dexpreopt/dexpreopt_test.go +++ b/dexpreopt/dexpreopt_test.go @@ -44,9 +44,7 @@ func testModuleConfig(ctx android.PathContext, name, partition string) *ModuleCo ProfileClassListing: android.OptionalPath{}, ProfileIsTextListing: false, EnforceUsesLibraries: false, - OptionalUsesLibraries: nil, - UsesLibraries: nil, - LibraryPaths: nil, + ClassLoaderContexts: nil, Archs: []android.ArchType{android.Arm}, DexPreoptImages: android.Paths{android.PathForTesting("system/framework/arm/boot.art")}, DexPreoptImagesDeps: []android.OutputPaths{android.OutputPaths{}}, diff --git a/java/aar.go b/java/aar.go index 157d677d3..c8faed0cd 100644 --- a/java/aar.go +++ b/java/aar.go @@ -109,7 +109,7 @@ type aapt struct { useEmbeddedNativeLibs bool useEmbeddedDex bool usesNonSdkApis bool - sdkLibraries dexpreopt.LibraryPaths + sdkLibraries dexpreopt.ClassLoaderContextMap hasNoCode bool LoggingParent string resourceFiles android.Paths @@ -392,7 +392,7 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, ex // aaptLibs collects libraries from dependencies and sdk_version and converts them into paths func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStaticLibs, transitiveStaticLibManifests android.Paths, - staticRRODirs []rroDir, assets, deps android.Paths, flags []string, sdkLibraries dexpreopt.LibraryPaths) { + staticRRODirs []rroDir, assets, deps android.Paths, flags []string, sdkLibraries dexpreopt.ClassLoaderContextMap) { var sharedLibs android.Paths @@ -401,7 +401,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati sharedLibs = append(sharedLibs, sdkDep.jars...) } - sdkLibraries = make(dexpreopt.LibraryPaths) + sdkLibraries = make(dexpreopt.ClassLoaderContextMap) ctx.VisitDirectDeps(func(module android.Module) { var exportPackage android.Path @@ -411,7 +411,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati } if dep, ok := module.(Dependency); ok { - sdkLibraries.AddLibraryPaths(dep.ExportedSdkLibs()) + sdkLibraries.AddContextMap(dep.ExportedSdkLibs()) } switch ctx.OtherModuleDependencyTag(module) { @@ -426,7 +426,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati // (including the java_sdk_library) itself then append any implicit sdk library // names to the list of sdk libraries to be added to the manifest. if component, ok := module.(SdkLibraryComponentDependency); ok { - sdkLibraries.MaybeAddLibraryPath(ctx, component.OptionalImplicitSdkLibrary(), + sdkLibraries.MaybeAddContext(ctx, component.OptionalImplicitSdkLibrary(), component.DexJarBuildPath(), component.DexJarInstallPath()) } @@ -439,7 +439,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...) transitiveStaticLibs = append(transitiveStaticLibs, exportPackage) transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...) - sdkLibraries.AddLibraryPaths(aarDep.ExportedSdkLibs()) + sdkLibraries.AddContextMap(aarDep.ExportedSdkLibs()) if aarDep.ExportedAssets().Valid() { assets = append(assets, aarDep.ExportedAssets().Path()) } @@ -827,7 +827,7 @@ func (a *AARImport) AidlIncludeDirs() android.Paths { return nil } -func (a *AARImport) ExportedSdkLibs() dexpreopt.LibraryPaths { +func (a *AARImport) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap { return nil } diff --git a/java/android_manifest.go b/java/android_manifest.go index 62cd11203..6b39c3584 100644 --- a/java/android_manifest.go +++ b/java/android_manifest.go @@ -43,8 +43,9 @@ var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger", "args", "libs") // Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml -func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, sdkLibraries dexpreopt.LibraryPaths, - isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path { +func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, + sdkLibraries dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, + useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path { var args []string if isLibrary { @@ -70,7 +71,7 @@ func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext args = append(args, "--use-embedded-dex") } - for _, usesLib := range android.SortedStringKeys(sdkLibraries) { + for _, usesLib := range sdkLibraries.UsesLibs() { if inList(usesLib, dexpreopt.OptionalCompatUsesLibs) { args = append(args, "--optional-uses-library", usesLib) } else { diff --git a/java/androidmk.go b/java/androidmk.go index e1a661fc1..c6062457f 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -115,7 +115,7 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile) } - entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", android.SortedStringKeys(library.exportedSdkLibs)...) + entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.exportedSdkLibs.UsesLibs()...) if len(library.additionalCheckedModules) != 0 { entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...) diff --git a/java/app.go b/java/app.go index c24e0c56b..4848faeac 100755 --- a/java/app.go +++ b/java/app.go @@ -601,7 +601,7 @@ func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk") } -func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext, sdkLibs dexpreopt.LibraryPaths) android.Path { +func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext, sdkLibs dexpreopt.ClassLoaderContextMap) android.Path { a.dexpreopter.installPath = a.installPath(ctx) if a.dexProperties.Uncompress_dex == nil { // If the value was not force-set by the user, use reasonable default based on the module. @@ -609,12 +609,10 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext, sdkLibs dexpreop } a.dexpreopter.uncompressedDex = *a.dexProperties.Uncompress_dex a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() - a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs - a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx) - a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx) - a.dexpreopter.libraryPaths.AddLibraryPaths(sdkLibs) + a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) + a.dexpreopter.classLoaderContexts.AddContextMap(sdkLibs) a.dexpreopter.manifestFile = a.mergedManifestFile - a.exportedSdkLibs = make(dexpreopt.LibraryPaths) + a.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap) if ctx.ModuleName() != "framework-res" { a.Module.compile(ctx, a.aaptSrcJar) @@ -791,7 +789,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { a.usesLibrary.freezeEnforceUsesLibraries() // Add implicit SDK libraries to list. - for _, usesLib := range android.SortedStringKeys(a.aapt.sdkLibraries) { + for _, usesLib := range a.aapt.sdkLibraries.UsesLibs() { a.usesLibrary.addLib(usesLib, inList(usesLib, dexpreopt.OptionalCompatUsesLibs)) } @@ -1540,9 +1538,7 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() - a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs - a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx) - a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx) + a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) dexOutput := a.dexpreopter.dexpreopt(ctx, jnisUncompressed) if a.dexpreopter.uncompressedDex { @@ -1976,17 +1972,18 @@ func (u *usesLibrary) presentOptionalUsesLibs(ctx android.BaseModuleContext) []s return optionalUsesLibs } -// usesLibraryPaths returns a map of module names of shared library dependencies to the paths +// Returns a map of module names of shared library dependencies to the paths // to their dex jars on host and on device. -func (u *usesLibrary) usesLibraryPaths(ctx android.ModuleContext) dexpreopt.LibraryPaths { - usesLibPaths := make(dexpreopt.LibraryPaths) +func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap { + clcMap := make(dexpreopt.ClassLoaderContextMap) if !ctx.Config().UnbundledBuild() { ctx.VisitDirectDeps(func(m android.Module) { - if _, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok { + if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok { dep := ctx.OtherModuleName(m) if lib, ok := m.(Dependency); ok { - usesLibPaths.AddLibraryPath(ctx, dep, lib.DexJarBuildPath(), lib.DexJarInstallPath()) + clcMap.AddContextForSdk(ctx, tag.sdkVersion, dep, + lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ExportedSdkLibs()) } else if ctx.Config().AllowMissingDependencies() { ctx.AddMissingDependencies([]string{dep}) } else { @@ -1996,7 +1993,7 @@ func (u *usesLibrary) usesLibraryPaths(ctx android.ModuleContext) dexpreopt.Libr }) } - return usesLibPaths + return clcMap } // enforceUsesLibraries returns true of tags should be checked against uses_libs and optional_uses_libs diff --git a/java/app_test.go b/java/app_test.go index 82577e32e..d9e4d3a69 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -2815,11 +2815,11 @@ func TestUsesLibraries(t *testing.T) { // Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs cmd = app.Rule("dexpreopt").RuleParams.Command w := `--target-context-for-sdk any ` + - `PCL[/system/framework/foo.jar]#` + - `PCL[/system/framework/quuz.jar]#` + `PCL[/system/framework/qux.jar]#` + - `PCL[/system/framework/runtime-library.jar]#` + - `PCL[/system/framework/bar.jar]` + `PCL[/system/framework/quuz.jar]#` + + `PCL[/system/framework/foo.jar]#` + + `PCL[/system/framework/bar.jar]#` + + `PCL[/system/framework/runtime-library.jar]` if !strings.Contains(cmd, w) { t.Errorf("wanted %q in %q", w, cmd) } diff --git a/java/device_host_converter.go b/java/device_host_converter.go index 40a2280d9..d8b617e7d 100644 --- a/java/device_host_converter.go +++ b/java/device_host_converter.go @@ -163,7 +163,7 @@ func (d *DeviceHostConverter) AidlIncludeDirs() android.Paths { return nil } -func (d *DeviceHostConverter) ExportedSdkLibs() dexpreopt.LibraryPaths { +func (d *DeviceHostConverter) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap { return nil } diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 20dbc666b..a21fb7640 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -33,11 +33,9 @@ type dexpreopter struct { isTest bool isPresignedPrebuilt bool - manifestFile android.Path - usesLibs []string - optionalUsesLibs []string - enforceUsesLibs bool - libraryPaths dexpreopt.LibraryPaths + manifestFile android.Path + enforceUsesLibs bool + classLoaderContexts dexpreopt.ClassLoaderContextMap builtInstalled string } @@ -193,10 +191,8 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo ProfileIsTextListing: profileIsTextListing, ProfileBootListing: profileBootListing, - EnforceUsesLibraries: d.enforceUsesLibs, - OptionalUsesLibraries: d.optionalUsesLibs, - UsesLibraries: d.usesLibs, - LibraryPaths: d.libraryPaths, + EnforceUsesLibraries: d.enforceUsesLibs, + ClassLoaderContexts: d.classLoaderContexts, Archs: archs, DexPreoptImages: images, diff --git a/java/java.go b/java/java.go index 9f0905126..31466b0bd 100644 --- a/java/java.go +++ b/java/java.go @@ -416,8 +416,8 @@ type Module struct { // manifest file to use instead of properties.Manifest overrideManifest android.OptionalPath - // map of SDK libs exported by this java module to their build and install paths - exportedSdkLibs dexpreopt.LibraryPaths + // map of SDK version to class loader context + exportedSdkLibs dexpreopt.ClassLoaderContextMap // list of plugins that this java module is exporting exportedPluginJars android.Paths @@ -509,7 +509,7 @@ type Dependency interface { ImplementationJars() android.Paths ResourceJars() android.Paths AidlIncludeDirs() android.Paths - ExportedSdkLibs() dexpreopt.LibraryPaths + ExportedSdkLibs() dexpreopt.ClassLoaderContextMap ExportedPlugins() (android.Paths, []string) SrcJarArgs() ([]string, android.Paths) BaseModuleName() string @@ -1027,7 +1027,8 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { case libTag: deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...) // names of sdk libs that are directly depended are exported - j.exportedSdkLibs.MaybeAddLibraryPath(ctx, dep.OptionalImplicitSdkLibrary(), dep.DexJarBuildPath(), dep.DexJarInstallPath()) + j.exportedSdkLibs.MaybeAddContext(ctx, dep.OptionalImplicitSdkLibrary(), + dep.DexJarBuildPath(), dep.DexJarInstallPath()) case staticLibTag: ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName) } @@ -1038,7 +1039,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { case libTag, instrumentationForTag: deps.classpath = append(deps.classpath, dep.HeaderJars()...) // sdk lib names from dependencies are re-exported - j.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs()) + j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs()) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) pluginJars, pluginClasses := dep.ExportedPlugins() addPlugins(&deps, pluginJars, pluginClasses...) @@ -1050,7 +1051,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...) deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars()...) // sdk lib names from dependencies are re-exported - j.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs()) + j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs()) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) pluginJars, pluginClasses := dep.ExportedPlugins() addPlugins(&deps, pluginJars, pluginClasses...) @@ -1902,7 +1903,7 @@ func (j *Module) AidlIncludeDirs() android.Paths { return j.exportAidlIncludeDirs } -func (j *Module) ExportedSdkLibs() dexpreopt.LibraryPaths { +func (j *Module) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap { return j.exportedSdkLibs } @@ -2041,7 +2042,7 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter)) } j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex - j.exportedSdkLibs = make(dexpreopt.LibraryPaths) + j.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap) j.compile(ctx, nil) // Collect the module directory for IDE info in java/jdeps.go. @@ -2061,11 +2062,12 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { // add the name of that java_sdk_library to the exported sdk libs to make sure // that, if necessary, a element for that java_sdk_library is // added to the Android manifest. - j.exportedSdkLibs.MaybeAddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), j.DexJarBuildPath(), j.DexJarInstallPath()) + j.exportedSdkLibs.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(), + j.DexJarBuildPath(), j.DexJarInstallPath()) // A non-SDK library may provide a (the name may be different from the module name). if lib := proptools.String(j.usesLibraryProperties.Provides_uses_lib); lib != "" { - j.exportedSdkLibs.AddLibraryPath(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath()) + j.exportedSdkLibs.AddContext(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath()) } j.distFiles = j.GenerateTaggedDistFiles(ctx) @@ -2644,7 +2646,7 @@ type Import struct { dexJarFile android.Path combinedClasspathFile android.Path - exportedSdkLibs dexpreopt.LibraryPaths + exportedSdkLibs dexpreopt.ClassLoaderContextMap exportAidlIncludeDirs android.Paths hideApexVariantFromMake bool @@ -2719,7 +2721,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { TransformJetifier(ctx, outputFile, inputFile) } j.combinedClasspathFile = outputFile - j.exportedSdkLibs = make(dexpreopt.LibraryPaths) + j.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap) var flags javaBuilderFlags @@ -2733,7 +2735,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { case libTag, staticLibTag: flags.classpath = append(flags.classpath, dep.HeaderJars()...) // sdk lib names from dependencies are re-exported - j.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs()) + j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs()) case bootClasspathTag: flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars()...) } @@ -2742,7 +2744,8 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { case libTag: flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...) // names of sdk libs that are directly depended are exported - j.exportedSdkLibs.AddLibraryPath(ctx, otherName, dep.DexJarBuildPath(), dep.DexJarInstallPath()) + j.exportedSdkLibs.AddContext(ctx, otherName, + dep.DexJarBuildPath(), dep.DexJarInstallPath()) } } }) @@ -2757,7 +2760,8 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { // add the name of that java_sdk_library to the exported sdk libs to make sure // that, if necessary, a element for that java_sdk_library is // added to the Android manifest. - j.exportedSdkLibs.MaybeAddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), outputFile, installFile) + j.exportedSdkLibs.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(), + outputFile, installFile) j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs) @@ -2839,7 +2843,7 @@ func (j *Import) AidlIncludeDirs() android.Paths { return j.exportAidlIncludeDirs } -func (j *Import) ExportedSdkLibs() dexpreopt.LibraryPaths { +func (j *Import) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap { return j.exportedSdkLibs } diff --git a/java/java_test.go b/java/java_test.go index 6c0a90856..81c4f22a8 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -1593,8 +1593,8 @@ func TestJavaSdkLibrary(t *testing.T) { // test if baz has exported SDK lib names foo and bar to qux qux := ctx.ModuleForTests("qux", "android_common") if quxLib, ok := qux.Module().(*Library); ok { - sdkLibs := android.SortedStringKeys(quxLib.ExportedSdkLibs()) - if w := []string{"bar", "foo", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) { + sdkLibs := quxLib.ExportedSdkLibs().UsesLibs() + if w := []string{"foo", "bar", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) { t.Errorf("qux should export %q but exports %q", w, sdkLibs) } }