Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 1 | // Copyright 2020 Google Inc. All rights reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package dexpreopt |
| 16 | |
| 17 | import ( |
| 18 | "fmt" |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 19 | "strconv" |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 20 | "strings" |
| 21 | |
| 22 | "android/soong/android" |
| 23 | ) |
| 24 | |
| 25 | // These libs are added as <uses-library> dependencies for apps if the targetSdkVersion in the |
| 26 | // app manifest is less than the specified version. This is needed because these libraries haven't |
| 27 | // existed prior to certain SDK version, but classes in them were in bootclasspath jars, etc. |
| 28 | // Some of the compatibility libraries are optional (their <uses-library> tag has "required=false"), |
| 29 | // so that if this library is missing this in not a build or run-time error. |
| 30 | var OrgApacheHttpLegacy = "org.apache.http.legacy" |
| 31 | var AndroidTestBase = "android.test.base" |
| 32 | var AndroidTestMock = "android.test.mock" |
| 33 | var AndroidHidlBase = "android.hidl.base-V1.0-java" |
| 34 | var AndroidHidlManager = "android.hidl.manager-V1.0-java" |
| 35 | |
| 36 | var OptionalCompatUsesLibs28 = []string{ |
| 37 | OrgApacheHttpLegacy, |
| 38 | } |
| 39 | var OptionalCompatUsesLibs30 = []string{ |
| 40 | AndroidTestBase, |
| 41 | AndroidTestMock, |
| 42 | } |
| 43 | var CompatUsesLibs29 = []string{ |
| 44 | AndroidHidlBase, |
| 45 | AndroidHidlManager, |
| 46 | } |
| 47 | var OptionalCompatUsesLibs = append(android.CopyOf(OptionalCompatUsesLibs28), OptionalCompatUsesLibs30...) |
| 48 | var CompatUsesLibs = android.CopyOf(CompatUsesLibs29) |
| 49 | |
| 50 | const UnknownInstallLibraryPath = "error" |
| 51 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 52 | // AnySdkVersion means that the class loader context is needed regardless of the targetSdkVersion |
| 53 | // of the app. The numeric value affects the key order in the map and, as a result, the order of |
| 54 | // arguments passed to construct_context.py (high value means that the unconditional context goes |
| 55 | // last). We use the converntional "current" SDK level (10000), but any big number would do as well. |
| 56 | const AnySdkVersion int = android.FutureApiLevelInt |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 57 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 58 | // ClassLoaderContext is a tree of libraries used by the dexpreopted module with their dependencies. |
| 59 | // The context is used by dex2oat to compile the module and recorded in the AOT-compiled files, so |
| 60 | // that it can be checked agains the run-time class loader context on device. If there is a mismatch |
| 61 | // at runtime, AOT-compiled code is rejected. |
| 62 | type ClassLoaderContext struct { |
| 63 | // The name of the library (same as the name of the module that contains it). |
| 64 | Name string |
| 65 | |
| 66 | // On-host build path to the library dex file (used in dex2oat argument --class-loader-context). |
| 67 | Host android.Path |
| 68 | |
| 69 | // On-device install path (used in dex2oat argument --stored-class-loader-context). |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 70 | Device string |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 71 | |
| 72 | // Nested class loader subcontexts for dependencies. |
| 73 | Subcontexts []*ClassLoaderContext |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 74 | } |
| 75 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 76 | // ClassLoaderContextMap is a map from SDK version to a class loader context. |
| 77 | // There is a special entry with key AnySdkVersion that stores unconditional class loader context. |
| 78 | // Other entries store conditional contexts that should be added for some apps that have |
| 79 | // targetSdkVersion in the manifest lower than the key SDK version. |
| 80 | type ClassLoaderContextMap map[int][]*ClassLoaderContext |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 81 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 82 | // Add class loader context for the given library to the map entry for the given SDK version. |
| 83 | func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string, |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 84 | hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error { |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 85 | |
| 86 | // If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is |
| 87 | // not found. However, this is likely to result is disabling dexpreopt, as it won't be |
| 88 | // possible to construct class loader context without on-host and on-device library paths. |
| 89 | strict = strict && !ctx.Config().AllowMissingDependencies() |
| 90 | |
| 91 | if hostPath == nil && strict { |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 92 | return fmt.Errorf("unknown build path to <uses-library> \"%s\"", lib) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 93 | } |
| 94 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 95 | devicePath := UnknownInstallLibraryPath |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 96 | if installPath == nil { |
| 97 | if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) { |
| 98 | // Assume that compatibility libraries are installed in /system/framework. |
| 99 | installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar") |
| 100 | } else if strict { |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 101 | return fmt.Errorf("unknown install path to <uses-library> \"%s\"", lib) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 102 | } else { |
| 103 | // For some stub libraries the only known thing is the name of their implementation |
| 104 | // library, but the library itself is unavailable (missing or part of a prebuilt). In |
| 105 | // such cases we still need to add the library to <uses-library> tags in the manifest, |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 106 | // but we cannot use it for dexpreopt. |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 107 | } |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 108 | } |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 109 | if installPath != nil { |
| 110 | devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath)) |
| 111 | } |
| 112 | |
Ulya Trafimovich | 5e13a73 | 2020-11-03 15:33:03 +0000 | [diff] [blame] | 113 | // Nested class loader context shouldn't have conditional part (it is allowed only at the top level). |
| 114 | for ver, _ := range nestedClcMap { |
| 115 | if ver != AnySdkVersion { |
| 116 | clcStr, _ := ComputeClassLoaderContext(nestedClcMap) |
| 117 | return fmt.Errorf("nested class loader context shouldn't have conditional part: %s", clcStr) |
| 118 | } |
| 119 | } |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 120 | subcontexts := nestedClcMap[AnySdkVersion] |
| 121 | |
| 122 | // If the library with this name is already present as one of the unconditional top-level |
| 123 | // components, do not re-add it. |
| 124 | for _, clc := range clcMap[sdkVer] { |
| 125 | if clc.Name == lib { |
| 126 | return nil |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{ |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 131 | Name: lib, |
| 132 | Host: hostPath, |
| 133 | Device: devicePath, |
| 134 | Subcontexts: subcontexts, |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 135 | }) |
Ulya Trafimovich | 6961267 | 2020-10-20 17:41:54 +0100 | [diff] [blame] | 136 | return nil |
| 137 | } |
| 138 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 139 | // Wrapper around addContext that reports errors. |
| 140 | func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string, |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 141 | hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) { |
Ulya Trafimovich | 6961267 | 2020-10-20 17:41:54 +0100 | [diff] [blame] | 142 | |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 143 | err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap) |
Ulya Trafimovich | 6961267 | 2020-10-20 17:41:54 +0100 | [diff] [blame] | 144 | if err != nil { |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 145 | ctx.ModuleErrorf(err.Error()) |
Ulya Trafimovich | 6961267 | 2020-10-20 17:41:54 +0100 | [diff] [blame] | 146 | } |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 147 | } |
| 148 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 149 | // Add class loader context. Fail on unknown build/install paths. |
| 150 | func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string, |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 151 | hostPath, installPath android.Path) { |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 152 | |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 153 | clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 154 | } |
| 155 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 156 | // Add class loader context if the library exists. Don't fail on unknown build/install paths. |
| 157 | func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string, |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 158 | hostPath, installPath android.Path) { |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 159 | |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 160 | if lib != nil { |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 161 | clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 162 | } |
| 163 | } |
| 164 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 165 | // Add class loader context for the given SDK version. Fail on unknown build/install paths. |
| 166 | func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int, |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 167 | lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) { |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 168 | |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 169 | clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, true, nestedClcMap) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 170 | } |
| 171 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 172 | // Merge the other class loader context map into this one, do not override existing entries. |
Ulya Trafimovich | 1855424 | 2020-11-03 15:55:11 +0000 | [diff] [blame] | 173 | // The implicitRootLib parameter is the name of the library for which the other class loader |
| 174 | // context map was constructed. If the implicitRootLib is itself a <uses-library>, it should be |
| 175 | // already present in the class loader context (with the other context as its subcontext) -- in |
| 176 | // that case do not re-add the other context. Otherwise add the other context at the top-level. |
| 177 | func (clcMap ClassLoaderContextMap) AddContextMap(otherClcMap ClassLoaderContextMap, implicitRootLib string) { |
| 178 | if otherClcMap == nil { |
| 179 | return |
| 180 | } |
| 181 | |
| 182 | // If the implicit root of the merged map is already present as one of top-level subtrees, do |
| 183 | // not merge it second time. |
| 184 | for _, clc := range clcMap[AnySdkVersion] { |
| 185 | if clc.Name == implicitRootLib { |
| 186 | return |
| 187 | } |
| 188 | } |
| 189 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 190 | for sdkVer, otherClcs := range otherClcMap { |
| 191 | for _, otherClc := range otherClcs { |
| 192 | alreadyHave := false |
| 193 | for _, clc := range clcMap[sdkVer] { |
| 194 | if clc.Name == otherClc.Name { |
| 195 | alreadyHave = true |
| 196 | break |
| 197 | } |
| 198 | } |
| 199 | if !alreadyHave { |
| 200 | clcMap[sdkVer] = append(clcMap[sdkVer], otherClc) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 201 | } |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 202 | } |
| 203 | } |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 204 | } |
| 205 | |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 206 | // Returns top-level libraries in the CLC (conditional CLC, i.e. compatibility libraries are not |
| 207 | // included). This is the list of libraries that should be in the <uses-library> tags in the |
| 208 | // manifest. Some of them may be present in the source manifest, others are added by manifest_fixer. |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 209 | func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) { |
| 210 | if clcMap != nil { |
Ulya Trafimovich | 78a7155 | 2020-11-25 14:20:52 +0000 | [diff] [blame^] | 211 | clcs := clcMap[AnySdkVersion] |
| 212 | ulibs = make([]string, 0, len(clcs)) |
| 213 | for _, clc := range clcs { |
| 214 | ulibs = append(ulibs, clc.Name) |
Ulya Trafimovich | a8c28e2 | 2020-10-06 17:24:19 +0100 | [diff] [blame] | 215 | } |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 216 | } |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 217 | return ulibs |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 218 | } |
| 219 | |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 220 | // Now that the full unconditional context is known, reconstruct conditional context. |
| 221 | // Apply filters for individual libraries, mirroring what the PackageManager does when it |
| 222 | // constructs class loader context on device. |
| 223 | // |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 224 | // TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps. |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 225 | // |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 226 | func fixClassLoaderContext(clcMap ClassLoaderContextMap) { |
| 227 | usesLibs := clcMap.UsesLibs() |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 228 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 229 | for sdkVer, clcs := range clcMap { |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 230 | if sdkVer == AnySdkVersion { |
| 231 | continue |
| 232 | } |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 233 | fixedClcs := []*ClassLoaderContext{} |
| 234 | for _, clc := range clcs { |
| 235 | if android.InList(clc.Name, usesLibs) { |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 236 | // skip compatibility libraries that are already included in unconditional context |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 237 | } else if clc.Name == AndroidTestMock && !android.InList("android.test.runner", usesLibs) { |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 238 | // android.test.mock is only needed as a compatibility library (in conditional class |
| 239 | // loader context) if android.test.runner is used, otherwise skip it |
| 240 | } else { |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 241 | fixedClcs = append(fixedClcs, clc) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 242 | } |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 243 | clcMap[sdkVer] = fixedClcs |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 248 | // Return true if all build/install library paths are valid (including recursive subcontexts), |
| 249 | // otherwise return false. A build path is valid if it's not nil. An install path is valid if it's |
| 250 | // not equal to a special "error" value. |
| 251 | func validateClassLoaderContext(clcMap ClassLoaderContextMap) (bool, error) { |
| 252 | for sdkVer, clcs := range clcMap { |
| 253 | if valid, err := validateClassLoaderContextRec(sdkVer, clcs); !valid || err != nil { |
| 254 | return valid, err |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 255 | } |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 256 | } |
| 257 | return true, nil |
| 258 | } |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 259 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 260 | func validateClassLoaderContextRec(sdkVer int, clcs []*ClassLoaderContext) (bool, error) { |
| 261 | for _, clc := range clcs { |
| 262 | if clc.Host == nil || clc.Device == UnknownInstallLibraryPath { |
| 263 | if sdkVer == AnySdkVersion { |
| 264 | // Return error if dexpreopt doesn't know paths to one of the <uses-library> |
| 265 | // dependencies. In the future we may need to relax this and just disable dexpreopt. |
| 266 | return false, fmt.Errorf("invalid path for <uses-library> \"%s\"", clc.Name) |
| 267 | } else { |
| 268 | // No error for compatibility libraries, as Soong doesn't know if they are needed |
| 269 | // (this depends on the targetSdkVersion in the manifest), but the CLC is invalid. |
| 270 | return false, nil |
| 271 | } |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 272 | } |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 273 | if valid, err := validateClassLoaderContextRec(sdkVer, clc.Subcontexts); !valid || err != nil { |
| 274 | return valid, err |
| 275 | } |
| 276 | } |
| 277 | return true, nil |
| 278 | } |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 279 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 280 | // Return the class loader context as a string, and a slice of build paths for all dependencies. |
| 281 | // Perform a depth-first preorder traversal of the class loader context tree for each SDK version. |
| 282 | // Return the resulting string and a slice of on-host build paths to all library dependencies. |
| 283 | func ComputeClassLoaderContext(clcMap ClassLoaderContextMap) (clcStr string, paths android.Paths) { |
| 284 | for _, sdkVer := range android.SortedIntKeys(clcMap) { // determinisitc traversal order |
| 285 | sdkVerStr := fmt.Sprintf("%d", sdkVer) |
| 286 | if sdkVer == AnySdkVersion { |
| 287 | sdkVerStr = "any" // a special keyword that means any SDK version |
| 288 | } |
| 289 | hostClc, targetClc, hostPaths := computeClassLoaderContextRec(clcMap[sdkVer]) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 290 | if hostPaths != nil { |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 291 | clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, hostClc) |
| 292 | clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, targetClc) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 293 | } |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 294 | paths = append(paths, hostPaths...) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 295 | } |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 296 | return clcStr, android.FirstUniquePaths(paths) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 297 | } |
| 298 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 299 | func computeClassLoaderContextRec(clcs []*ClassLoaderContext) (string, string, android.Paths) { |
| 300 | var paths android.Paths |
| 301 | var clcsHost, clcsTarget []string |
| 302 | |
| 303 | for _, clc := range clcs { |
| 304 | subClcHost, subClcTarget, subPaths := computeClassLoaderContextRec(clc.Subcontexts) |
| 305 | if subPaths != nil { |
| 306 | subClcHost = "{" + subClcHost + "}" |
| 307 | subClcTarget = "{" + subClcTarget + "}" |
| 308 | } |
| 309 | |
| 310 | clcsHost = append(clcsHost, "PCL["+clc.Host.String()+"]"+subClcHost) |
| 311 | clcsTarget = append(clcsTarget, "PCL["+clc.Device+"]"+subClcTarget) |
| 312 | |
| 313 | paths = append(paths, clc.Host) |
| 314 | paths = append(paths, subPaths...) |
| 315 | } |
| 316 | |
| 317 | clcHost := strings.Join(clcsHost, "#") |
| 318 | clcTarget := strings.Join(clcsTarget, "#") |
| 319 | |
| 320 | return clcHost, clcTarget, paths |
| 321 | } |
| 322 | |
| 323 | // Paths to a <uses-library> on host and on device. |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 324 | type jsonLibraryPath struct { |
| 325 | Host string |
| 326 | Device string |
| 327 | } |
| 328 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 329 | // Class loader contexts that come from Make (via JSON dexpreopt.config) files have simpler |
| 330 | // structure than Soong class loader contexts: they are flat maps from a <uses-library> name to its |
| 331 | // on-host and on-device paths. There are no nested subcontexts. It is a limitation of the current |
| 332 | // Make implementation. |
| 333 | type jsonClassLoaderContext map[string]jsonLibraryPath |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 334 | |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 335 | // A map from SDK version (represented with a JSON string) to JSON class loader context. |
| 336 | type jsonClassLoaderContextMap map[string]jsonClassLoaderContext |
| 337 | |
| 338 | // Convert JSON class loader context map to ClassLoaderContextMap. |
| 339 | func fromJsonClassLoaderContext(ctx android.PathContext, jClcMap jsonClassLoaderContextMap) ClassLoaderContextMap { |
| 340 | clcMap := make(ClassLoaderContextMap) |
| 341 | for sdkVerStr, clc := range jClcMap { |
| 342 | sdkVer, ok := strconv.Atoi(sdkVerStr) |
| 343 | if ok != nil { |
| 344 | if sdkVerStr == "any" { |
| 345 | sdkVer = AnySdkVersion |
| 346 | } else { |
| 347 | android.ReportPathErrorf(ctx, "failed to parse SDK version in dexpreopt.config: '%s'", sdkVerStr) |
| 348 | } |
| 349 | } |
| 350 | for lib, path := range clc { |
| 351 | clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{ |
| 352 | Name: lib, |
| 353 | Host: constructPath(ctx, path.Host), |
| 354 | Device: path.Device, |
| 355 | Subcontexts: nil, |
| 356 | }) |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 357 | } |
| 358 | } |
Ulya Trafimovich | 8cbc5d2 | 2020-11-03 15:15:46 +0000 | [diff] [blame] | 359 | return clcMap |
Ulya Trafimovich | eb26886 | 2020-10-20 15:16:38 +0100 | [diff] [blame] | 360 | } |