blob: 8d61901905e21cee658aacb6480f94ee1f0b6b0d [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,
84 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
113 subcontexts := nestedClcMap[AnySdkVersion]
114
115 // If the library with this name is already present as one of the unconditional top-level
116 // components, do not re-add it.
117 for _, clc := range clcMap[sdkVer] {
118 if clc.Name == lib {
119 return nil
120 }
121 }
122
123 clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
124 Name: lib,
125 Host: hostPath,
126 Device: devicePath,
127 Subcontexts: subcontexts,
128 })
Ulya Trafimovich69612672020-10-20 17:41:54 +0100129 return nil
130}
131
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000132// Wrapper around addContext that reports errors.
133func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
134 hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
Ulya Trafimovich69612672020-10-20 17:41:54 +0100135
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000136 err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap)
Ulya Trafimovich69612672020-10-20 17:41:54 +0100137 if err != nil {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000138 ctx.ModuleErrorf(err.Error())
Ulya Trafimovich69612672020-10-20 17:41:54 +0100139 android.ReportPathErrorf(ctx, err.Error())
140 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100141}
142
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000143// Add class loader context. Fail on unknown build/install paths.
144func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
145 hostPath, installPath android.Path) {
146
147 clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100148}
149
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000150// Add class loader context if the library exists. Don't fail on unknown build/install paths.
151func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
152 hostPath, installPath android.Path) {
153
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100154 if lib != nil {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000155 clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100156 }
157}
158
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000159// Add class loader context for the given SDK version. Fail on unknown build/install paths.
160func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
161 lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
162
163 clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, true, nestedClcMap)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100164}
165
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000166// Merge the other class loader context map into this one, do not override existing entries.
167func (clcMap ClassLoaderContextMap) AddContextMap(otherClcMap ClassLoaderContextMap) {
168 for sdkVer, otherClcs := range otherClcMap {
169 for _, otherClc := range otherClcs {
170 alreadyHave := false
171 for _, clc := range clcMap[sdkVer] {
172 if clc.Name == otherClc.Name {
173 alreadyHave = true
174 break
175 }
176 }
177 if !alreadyHave {
178 clcMap[sdkVer] = append(clcMap[sdkVer], otherClc)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100179 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100180 }
181 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100182}
183
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000184// List of libraries in the unconditional class loader context, excluding dependencies of shared libraries.
185func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) {
186 if clcMap != nil {
187 // compatibility libraries (those in conditional context) are not added to <uses-library> tags
188 ulibs = usesLibsRec(clcMap[AnySdkVersion])
189 ulibs = android.FirstUniqueStrings(ulibs)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100190 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000191 return ulibs
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100192}
193
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000194func usesLibsRec(clcs []*ClassLoaderContext) (ulibs []string) {
195 for _, clc := range clcs {
196 ulibs = append(ulibs, clc.Name)
197 ulibs = append(ulibs, usesLibsRec(clc.Subcontexts)...)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100198 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000199 return ulibs
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100200}
201
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100202// Now that the full unconditional context is known, reconstruct conditional context.
203// Apply filters for individual libraries, mirroring what the PackageManager does when it
204// constructs class loader context on device.
205//
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000206// TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps.
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100207//
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000208func fixClassLoaderContext(clcMap ClassLoaderContextMap) {
209 usesLibs := clcMap.UsesLibs()
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100210
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000211 for sdkVer, clcs := range clcMap {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100212 if sdkVer == AnySdkVersion {
213 continue
214 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000215 fixedClcs := []*ClassLoaderContext{}
216 for _, clc := range clcs {
217 if android.InList(clc.Name, usesLibs) {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100218 // skip compatibility libraries that are already included in unconditional context
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000219 } else if clc.Name == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100220 // android.test.mock is only needed as a compatibility library (in conditional class
221 // loader context) if android.test.runner is used, otherwise skip it
222 } else {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000223 fixedClcs = append(fixedClcs, clc)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100224 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000225 clcMap[sdkVer] = fixedClcs
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100226 }
227 }
228}
229
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000230// Return true if all build/install library paths are valid (including recursive subcontexts),
231// otherwise return false. A build path is valid if it's not nil. An install path is valid if it's
232// not equal to a special "error" value.
233func validateClassLoaderContext(clcMap ClassLoaderContextMap) (bool, error) {
234 for sdkVer, clcs := range clcMap {
235 if valid, err := validateClassLoaderContextRec(sdkVer, clcs); !valid || err != nil {
236 return valid, err
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100237 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000238 }
239 return true, nil
240}
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100241
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000242func validateClassLoaderContextRec(sdkVer int, clcs []*ClassLoaderContext) (bool, error) {
243 for _, clc := range clcs {
244 if clc.Host == nil || clc.Device == UnknownInstallLibraryPath {
245 if sdkVer == AnySdkVersion {
246 // Return error if dexpreopt doesn't know paths to one of the <uses-library>
247 // dependencies. In the future we may need to relax this and just disable dexpreopt.
248 return false, fmt.Errorf("invalid path for <uses-library> \"%s\"", clc.Name)
249 } else {
250 // No error for compatibility libraries, as Soong doesn't know if they are needed
251 // (this depends on the targetSdkVersion in the manifest), but the CLC is invalid.
252 return false, nil
253 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100254 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000255 if valid, err := validateClassLoaderContextRec(sdkVer, clc.Subcontexts); !valid || err != nil {
256 return valid, err
257 }
258 }
259 return true, nil
260}
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100261
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000262// Return the class loader context as a string, and a slice of build paths for all dependencies.
263// Perform a depth-first preorder traversal of the class loader context tree for each SDK version.
264// Return the resulting string and a slice of on-host build paths to all library dependencies.
265func ComputeClassLoaderContext(clcMap ClassLoaderContextMap) (clcStr string, paths android.Paths) {
266 for _, sdkVer := range android.SortedIntKeys(clcMap) { // determinisitc traversal order
267 sdkVerStr := fmt.Sprintf("%d", sdkVer)
268 if sdkVer == AnySdkVersion {
269 sdkVerStr = "any" // a special keyword that means any SDK version
270 }
271 hostClc, targetClc, hostPaths := computeClassLoaderContextRec(clcMap[sdkVer])
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100272 if hostPaths != nil {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000273 clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, hostClc)
274 clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, targetClc)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100275 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000276 paths = append(paths, hostPaths...)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100277 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000278 return clcStr, android.FirstUniquePaths(paths)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100279}
280
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000281func computeClassLoaderContextRec(clcs []*ClassLoaderContext) (string, string, android.Paths) {
282 var paths android.Paths
283 var clcsHost, clcsTarget []string
284
285 for _, clc := range clcs {
286 subClcHost, subClcTarget, subPaths := computeClassLoaderContextRec(clc.Subcontexts)
287 if subPaths != nil {
288 subClcHost = "{" + subClcHost + "}"
289 subClcTarget = "{" + subClcTarget + "}"
290 }
291
292 clcsHost = append(clcsHost, "PCL["+clc.Host.String()+"]"+subClcHost)
293 clcsTarget = append(clcsTarget, "PCL["+clc.Device+"]"+subClcTarget)
294
295 paths = append(paths, clc.Host)
296 paths = append(paths, subPaths...)
297 }
298
299 clcHost := strings.Join(clcsHost, "#")
300 clcTarget := strings.Join(clcsTarget, "#")
301
302 return clcHost, clcTarget, paths
303}
304
305// Paths to a <uses-library> on host and on device.
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100306type jsonLibraryPath struct {
307 Host string
308 Device string
309}
310
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000311// Class loader contexts that come from Make (via JSON dexpreopt.config) files have simpler
312// structure than Soong class loader contexts: they are flat maps from a <uses-library> name to its
313// on-host and on-device paths. There are no nested subcontexts. It is a limitation of the current
314// Make implementation.
315type jsonClassLoaderContext map[string]jsonLibraryPath
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100316
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000317// A map from SDK version (represented with a JSON string) to JSON class loader context.
318type jsonClassLoaderContextMap map[string]jsonClassLoaderContext
319
320// Convert JSON class loader context map to ClassLoaderContextMap.
321func fromJsonClassLoaderContext(ctx android.PathContext, jClcMap jsonClassLoaderContextMap) ClassLoaderContextMap {
322 clcMap := make(ClassLoaderContextMap)
323 for sdkVerStr, clc := range jClcMap {
324 sdkVer, ok := strconv.Atoi(sdkVerStr)
325 if ok != nil {
326 if sdkVerStr == "any" {
327 sdkVer = AnySdkVersion
328 } else {
329 android.ReportPathErrorf(ctx, "failed to parse SDK version in dexpreopt.config: '%s'", sdkVerStr)
330 }
331 }
332 for lib, path := range clc {
333 clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
334 Name: lib,
335 Host: constructPath(ctx, path.Host),
336 Device: path.Device,
337 Subcontexts: nil,
338 })
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100339 }
340 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000341 return clcMap
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100342}