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 446050d7f..6429ab836 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 2a27922d1..4594b8111 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) } }