blob: 22f712c0ede0f83f0af93254c210f86227906cf9 [file] [log] [blame]
Ulya Trafimovicheb268862020-10-20 15:16:38 +01001// 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
15package dexpreopt
16
17import (
18 "fmt"
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000019 "strconv"
Ulya Trafimovicheb268862020-10-20 15:16:38 +010020 "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.
30var OrgApacheHttpLegacy = "org.apache.http.legacy"
31var AndroidTestBase = "android.test.base"
32var AndroidTestMock = "android.test.mock"
33var AndroidHidlBase = "android.hidl.base-V1.0-java"
34var AndroidHidlManager = "android.hidl.manager-V1.0-java"
35
36var OptionalCompatUsesLibs28 = []string{
37 OrgApacheHttpLegacy,
38}
39var OptionalCompatUsesLibs30 = []string{
40 AndroidTestBase,
41 AndroidTestMock,
42}
43var CompatUsesLibs29 = []string{
44 AndroidHidlBase,
45 AndroidHidlManager,
46}
47var OptionalCompatUsesLibs = append(android.CopyOf(OptionalCompatUsesLibs28), OptionalCompatUsesLibs30...)
48var CompatUsesLibs = android.CopyOf(CompatUsesLibs29)
49
50const UnknownInstallLibraryPath = "error"
51
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000052// 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.
56const AnySdkVersion int = android.FutureApiLevelInt
Ulya Trafimovicheb268862020-10-20 15:16:38 +010057
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000058// 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.
62type 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 Trafimovicheb268862020-10-20 15:16:38 +010070 Device string
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000071
72 // Nested class loader subcontexts for dependencies.
73 Subcontexts []*ClassLoaderContext
Ulya Trafimovicheb268862020-10-20 15:16:38 +010074}
75
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000076// 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.
80type ClassLoaderContextMap map[int][]*ClassLoaderContext
Ulya Trafimovicheb268862020-10-20 15:16:38 +010081
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000082// Add class loader context for the given library to the map entry for the given SDK version.
83func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
Ulya Trafimovich78a71552020-11-25 14:20:52 +000084 hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error {
Ulya Trafimovicheb268862020-10-20 15:16:38 +010085
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 Trafimovich8cbc5d22020-11-03 15:15:46 +000092 return fmt.Errorf("unknown build path to <uses-library> \"%s\"", lib)
Ulya Trafimovicheb268862020-10-20 15:16:38 +010093 }
94
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000095 devicePath := UnknownInstallLibraryPath
Ulya Trafimovicheb268862020-10-20 15:16:38 +010096 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 Trafimovich8cbc5d22020-11-03 15:15:46 +0000101 return fmt.Errorf("unknown install path to <uses-library> \"%s\"", lib)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100102 } 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 Trafimovich8cbc5d22020-11-03 15:15:46 +0000106 // but we cannot use it for dexpreopt.
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100107 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100108 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000109 if installPath != nil {
110 devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
111 }
112
Ulya Trafimovich5e13a732020-11-03 15:33:03 +0000113 // 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 Trafimovich8cbc5d22020-11-03 15:15:46 +0000120 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 Trafimovich78a71552020-11-25 14:20:52 +0000131 Name: lib,
132 Host: hostPath,
133 Device: devicePath,
134 Subcontexts: subcontexts,
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000135 })
Ulya Trafimovich69612672020-10-20 17:41:54 +0100136 return nil
137}
138
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000139// Wrapper around addContext that reports errors.
140func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000141 hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
Ulya Trafimovich69612672020-10-20 17:41:54 +0100142
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000143 err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap)
Ulya Trafimovich69612672020-10-20 17:41:54 +0100144 if err != nil {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000145 ctx.ModuleErrorf(err.Error())
Ulya Trafimovich69612672020-10-20 17:41:54 +0100146 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100147}
148
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000149// Add class loader context. Fail on unknown build/install paths.
150func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000151 hostPath, installPath android.Path) {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000152
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000153 clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100154}
155
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000156// Add class loader context if the library exists. Don't fail on unknown build/install paths.
157func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000158 hostPath, installPath android.Path) {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000159
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100160 if lib != nil {
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000161 clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100162 }
163}
164
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000165// Add class loader context for the given SDK version. Fail on unknown build/install paths.
166func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000167 lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000168
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000169 clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, true, nestedClcMap)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100170}
171
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000172// Merge the other class loader context map into this one, do not override existing entries.
Ulya Trafimovich18554242020-11-03 15:55:11 +0000173// 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.
177func (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 Trafimovich8cbc5d22020-11-03 15:15:46 +0000190 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 Trafimovicheb268862020-10-20 15:16:38 +0100201 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100202 }
203 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100204}
205
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000206// 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 Trafimovich8cbc5d22020-11-03 15:15:46 +0000209func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) {
210 if clcMap != nil {
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000211 clcs := clcMap[AnySdkVersion]
212 ulibs = make([]string, 0, len(clcs))
213 for _, clc := range clcs {
214 ulibs = append(ulibs, clc.Name)
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100215 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100216 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000217 return ulibs
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100218}
219
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100220// 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 Trafimovich8cbc5d22020-11-03 15:15:46 +0000224// TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps.
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100225//
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000226func fixClassLoaderContext(clcMap ClassLoaderContextMap) {
227 usesLibs := clcMap.UsesLibs()
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100228
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000229 for sdkVer, clcs := range clcMap {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100230 if sdkVer == AnySdkVersion {
231 continue
232 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000233 fixedClcs := []*ClassLoaderContext{}
234 for _, clc := range clcs {
235 if android.InList(clc.Name, usesLibs) {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100236 // skip compatibility libraries that are already included in unconditional context
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000237 } else if clc.Name == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100238 // 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 Trafimovich8cbc5d22020-11-03 15:15:46 +0000241 fixedClcs = append(fixedClcs, clc)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100242 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000243 clcMap[sdkVer] = fixedClcs
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100244 }
245 }
246}
247
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000248// 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.
251func 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 Trafimovicheb268862020-10-20 15:16:38 +0100255 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000256 }
257 return true, nil
258}
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100259
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000260func 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 Trafimovicheb268862020-10-20 15:16:38 +0100272 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000273 if valid, err := validateClassLoaderContextRec(sdkVer, clc.Subcontexts); !valid || err != nil {
274 return valid, err
275 }
276 }
277 return true, nil
278}
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100279
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000280// 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.
283func 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 Trafimovicheb268862020-10-20 15:16:38 +0100290 if hostPaths != nil {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000291 clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, hostClc)
292 clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, targetClc)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100293 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000294 paths = append(paths, hostPaths...)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100295 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000296 return clcStr, android.FirstUniquePaths(paths)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100297}
298
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000299func 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 Trafimovicheb268862020-10-20 15:16:38 +0100324type jsonLibraryPath struct {
325 Host string
326 Device string
327}
328
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000329// 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.
333type jsonClassLoaderContext map[string]jsonLibraryPath
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100334
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000335// A map from SDK version (represented with a JSON string) to JSON class loader context.
336type jsonClassLoaderContextMap map[string]jsonClassLoaderContext
337
338// Convert JSON class loader context map to ClassLoaderContextMap.
339func 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 Trafimovicheb268862020-10-20 15:16:38 +0100357 }
358 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000359 return clcMap
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100360}