blob: 2b6ec79bbdeeba2bd196f25c5b66a4f5639ab144 [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
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{
131 Name: lib,
132 Host: hostPath,
133 Device: devicePath,
134 Subcontexts: subcontexts,
135 })
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,
141 hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
Ulya Trafimovich69612672020-10-20 17:41:54 +0100142
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +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 android.ReportPathErrorf(ctx, err.Error())
147 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100148}
149
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000150// Add class loader context. Fail on unknown build/install paths.
151func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
152 hostPath, installPath android.Path) {
153
154 clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100155}
156
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000157// Add class loader context if the library exists. Don't fail on unknown build/install paths.
158func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
159 hostPath, installPath android.Path) {
160
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100161 if lib != nil {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000162 clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100163 }
164}
165
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000166// Add class loader context for the given SDK version. Fail on unknown build/install paths.
167func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
168 lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
169
170 clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, true, nestedClcMap)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100171}
172
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000173// Merge the other class loader context map into this one, do not override existing entries.
174func (clcMap ClassLoaderContextMap) AddContextMap(otherClcMap ClassLoaderContextMap) {
175 for sdkVer, otherClcs := range otherClcMap {
176 for _, otherClc := range otherClcs {
177 alreadyHave := false
178 for _, clc := range clcMap[sdkVer] {
179 if clc.Name == otherClc.Name {
180 alreadyHave = true
181 break
182 }
183 }
184 if !alreadyHave {
185 clcMap[sdkVer] = append(clcMap[sdkVer], otherClc)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100186 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100187 }
188 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100189}
190
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000191// List of libraries in the unconditional class loader context, excluding dependencies of shared libraries.
192func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) {
193 if clcMap != nil {
194 // compatibility libraries (those in conditional context) are not added to <uses-library> tags
195 ulibs = usesLibsRec(clcMap[AnySdkVersion])
196 ulibs = android.FirstUniqueStrings(ulibs)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100197 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000198 return ulibs
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100199}
200
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000201func usesLibsRec(clcs []*ClassLoaderContext) (ulibs []string) {
202 for _, clc := range clcs {
203 ulibs = append(ulibs, clc.Name)
204 ulibs = append(ulibs, usesLibsRec(clc.Subcontexts)...)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100205 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000206 return ulibs
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100207}
208
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100209// Now that the full unconditional context is known, reconstruct conditional context.
210// Apply filters for individual libraries, mirroring what the PackageManager does when it
211// constructs class loader context on device.
212//
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000213// TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps.
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100214//
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000215func fixClassLoaderContext(clcMap ClassLoaderContextMap) {
216 usesLibs := clcMap.UsesLibs()
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100217
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000218 for sdkVer, clcs := range clcMap {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100219 if sdkVer == AnySdkVersion {
220 continue
221 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000222 fixedClcs := []*ClassLoaderContext{}
223 for _, clc := range clcs {
224 if android.InList(clc.Name, usesLibs) {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100225 // skip compatibility libraries that are already included in unconditional context
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000226 } else if clc.Name == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100227 // android.test.mock is only needed as a compatibility library (in conditional class
228 // loader context) if android.test.runner is used, otherwise skip it
229 } else {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000230 fixedClcs = append(fixedClcs, clc)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100231 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000232 clcMap[sdkVer] = fixedClcs
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100233 }
234 }
235}
236
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000237// Return true if all build/install library paths are valid (including recursive subcontexts),
238// otherwise return false. A build path is valid if it's not nil. An install path is valid if it's
239// not equal to a special "error" value.
240func validateClassLoaderContext(clcMap ClassLoaderContextMap) (bool, error) {
241 for sdkVer, clcs := range clcMap {
242 if valid, err := validateClassLoaderContextRec(sdkVer, clcs); !valid || err != nil {
243 return valid, err
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100244 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000245 }
246 return true, nil
247}
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100248
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000249func validateClassLoaderContextRec(sdkVer int, clcs []*ClassLoaderContext) (bool, error) {
250 for _, clc := range clcs {
251 if clc.Host == nil || clc.Device == UnknownInstallLibraryPath {
252 if sdkVer == AnySdkVersion {
253 // Return error if dexpreopt doesn't know paths to one of the <uses-library>
254 // dependencies. In the future we may need to relax this and just disable dexpreopt.
255 return false, fmt.Errorf("invalid path for <uses-library> \"%s\"", clc.Name)
256 } else {
257 // No error for compatibility libraries, as Soong doesn't know if they are needed
258 // (this depends on the targetSdkVersion in the manifest), but the CLC is invalid.
259 return false, nil
260 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100261 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000262 if valid, err := validateClassLoaderContextRec(sdkVer, clc.Subcontexts); !valid || err != nil {
263 return valid, err
264 }
265 }
266 return true, nil
267}
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100268
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000269// Return the class loader context as a string, and a slice of build paths for all dependencies.
270// Perform a depth-first preorder traversal of the class loader context tree for each SDK version.
271// Return the resulting string and a slice of on-host build paths to all library dependencies.
272func ComputeClassLoaderContext(clcMap ClassLoaderContextMap) (clcStr string, paths android.Paths) {
273 for _, sdkVer := range android.SortedIntKeys(clcMap) { // determinisitc traversal order
274 sdkVerStr := fmt.Sprintf("%d", sdkVer)
275 if sdkVer == AnySdkVersion {
276 sdkVerStr = "any" // a special keyword that means any SDK version
277 }
278 hostClc, targetClc, hostPaths := computeClassLoaderContextRec(clcMap[sdkVer])
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100279 if hostPaths != nil {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000280 clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, hostClc)
281 clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, targetClc)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100282 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000283 paths = append(paths, hostPaths...)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100284 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000285 return clcStr, android.FirstUniquePaths(paths)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100286}
287
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000288func computeClassLoaderContextRec(clcs []*ClassLoaderContext) (string, string, android.Paths) {
289 var paths android.Paths
290 var clcsHost, clcsTarget []string
291
292 for _, clc := range clcs {
293 subClcHost, subClcTarget, subPaths := computeClassLoaderContextRec(clc.Subcontexts)
294 if subPaths != nil {
295 subClcHost = "{" + subClcHost + "}"
296 subClcTarget = "{" + subClcTarget + "}"
297 }
298
299 clcsHost = append(clcsHost, "PCL["+clc.Host.String()+"]"+subClcHost)
300 clcsTarget = append(clcsTarget, "PCL["+clc.Device+"]"+subClcTarget)
301
302 paths = append(paths, clc.Host)
303 paths = append(paths, subPaths...)
304 }
305
306 clcHost := strings.Join(clcsHost, "#")
307 clcTarget := strings.Join(clcsTarget, "#")
308
309 return clcHost, clcTarget, paths
310}
311
312// Paths to a <uses-library> on host and on device.
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100313type jsonLibraryPath struct {
314 Host string
315 Device string
316}
317
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000318// Class loader contexts that come from Make (via JSON dexpreopt.config) files have simpler
319// structure than Soong class loader contexts: they are flat maps from a <uses-library> name to its
320// on-host and on-device paths. There are no nested subcontexts. It is a limitation of the current
321// Make implementation.
322type jsonClassLoaderContext map[string]jsonLibraryPath
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100323
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000324// A map from SDK version (represented with a JSON string) to JSON class loader context.
325type jsonClassLoaderContextMap map[string]jsonClassLoaderContext
326
327// Convert JSON class loader context map to ClassLoaderContextMap.
328func fromJsonClassLoaderContext(ctx android.PathContext, jClcMap jsonClassLoaderContextMap) ClassLoaderContextMap {
329 clcMap := make(ClassLoaderContextMap)
330 for sdkVerStr, clc := range jClcMap {
331 sdkVer, ok := strconv.Atoi(sdkVerStr)
332 if ok != nil {
333 if sdkVerStr == "any" {
334 sdkVer = AnySdkVersion
335 } else {
336 android.ReportPathErrorf(ctx, "failed to parse SDK version in dexpreopt.config: '%s'", sdkVerStr)
337 }
338 }
339 for lib, path := range clc {
340 clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
341 Name: lib,
342 Host: constructPath(ctx, path.Host),
343 Device: path.Device,
344 Subcontexts: nil,
345 })
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100346 }
347 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000348 return clcMap
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100349}