blob: b910a70e888eeaa82fce84bf1ca15a2fd09f2222 [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 Trafimovicha8c28e22020-10-06 17:24:19 +010074
75 // If the library is a shared library. This affects which elements of class loader context are
76 // added as <uses-library> tags by the manifest_fixer (dependencies of shared libraries aren't).
77 IsSharedLibrary bool
Ulya Trafimovicheb268862020-10-20 15:16:38 +010078}
79
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000080// ClassLoaderContextMap is a map from SDK version to a class loader context.
81// There is a special entry with key AnySdkVersion that stores unconditional class loader context.
82// Other entries store conditional contexts that should be added for some apps that have
83// targetSdkVersion in the manifest lower than the key SDK version.
84type ClassLoaderContextMap map[int][]*ClassLoaderContext
Ulya Trafimovicheb268862020-10-20 15:16:38 +010085
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000086// Add class loader context for the given library to the map entry for the given SDK version.
87func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +010088 shared bool, hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error {
Ulya Trafimovicheb268862020-10-20 15:16:38 +010089
90 // If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
91 // not found. However, this is likely to result is disabling dexpreopt, as it won't be
92 // possible to construct class loader context without on-host and on-device library paths.
93 strict = strict && !ctx.Config().AllowMissingDependencies()
94
95 if hostPath == nil && strict {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000096 return fmt.Errorf("unknown build path to <uses-library> \"%s\"", lib)
Ulya Trafimovicheb268862020-10-20 15:16:38 +010097 }
98
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000099 devicePath := UnknownInstallLibraryPath
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100100 if installPath == nil {
101 if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
102 // Assume that compatibility libraries are installed in /system/framework.
103 installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
104 } else if strict {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000105 return fmt.Errorf("unknown install path to <uses-library> \"%s\"", lib)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100106 } else {
107 // For some stub libraries the only known thing is the name of their implementation
108 // library, but the library itself is unavailable (missing or part of a prebuilt). In
109 // such cases we still need to add the library to <uses-library> tags in the manifest,
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000110 // but we cannot use it for dexpreopt.
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100111 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100112 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000113 if installPath != nil {
114 devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
115 }
116
Ulya Trafimovich5e13a732020-11-03 15:33:03 +0000117 // Nested class loader context shouldn't have conditional part (it is allowed only at the top level).
118 for ver, _ := range nestedClcMap {
119 if ver != AnySdkVersion {
120 clcStr, _ := ComputeClassLoaderContext(nestedClcMap)
121 return fmt.Errorf("nested class loader context shouldn't have conditional part: %s", clcStr)
122 }
123 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000124 subcontexts := nestedClcMap[AnySdkVersion]
125
126 // If the library with this name is already present as one of the unconditional top-level
127 // components, do not re-add it.
128 for _, clc := range clcMap[sdkVer] {
129 if clc.Name == lib {
130 return nil
131 }
132 }
133
134 clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100135 Name: lib,
136 Host: hostPath,
137 Device: devicePath,
138 Subcontexts: subcontexts,
139 IsSharedLibrary: shared,
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000140 })
Ulya Trafimovich69612672020-10-20 17:41:54 +0100141 return nil
142}
143
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000144// Wrapper around addContext that reports errors.
145func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100146 shared bool, hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
Ulya Trafimovich69612672020-10-20 17:41:54 +0100147
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100148 err := clcMap.addContext(ctx, sdkVer, lib, shared, hostPath, installPath, strict, nestedClcMap)
Ulya Trafimovich69612672020-10-20 17:41:54 +0100149 if err != nil {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000150 ctx.ModuleErrorf(err.Error())
Ulya Trafimovich69612672020-10-20 17:41:54 +0100151 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100152}
153
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000154// Add class loader context. Fail on unknown build/install paths.
155func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100156 shared bool, hostPath, installPath android.Path) {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000157
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100158 clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, shared, hostPath, installPath, true, nil)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100159}
160
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000161// Add class loader context if the library exists. Don't fail on unknown build/install paths.
162func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100163 shared bool, hostPath, installPath android.Path) {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000164
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100165 if lib != nil {
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100166 clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, shared, hostPath, installPath, false, nil)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100167 }
168}
169
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000170// Add class loader context for the given SDK version. Fail on unknown build/install paths.
171func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100172 lib string, shared bool, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000173
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100174 clcMap.addContextOrReportError(ctx, sdkVer, lib, shared, hostPath, installPath, true, nestedClcMap)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100175}
176
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000177// Merge the other class loader context map into this one, do not override existing entries.
Ulya Trafimovich18554242020-11-03 15:55:11 +0000178// The implicitRootLib parameter is the name of the library for which the other class loader
179// context map was constructed. If the implicitRootLib is itself a <uses-library>, it should be
180// already present in the class loader context (with the other context as its subcontext) -- in
181// that case do not re-add the other context. Otherwise add the other context at the top-level.
182func (clcMap ClassLoaderContextMap) AddContextMap(otherClcMap ClassLoaderContextMap, implicitRootLib string) {
183 if otherClcMap == nil {
184 return
185 }
186
187 // If the implicit root of the merged map is already present as one of top-level subtrees, do
188 // not merge it second time.
189 for _, clc := range clcMap[AnySdkVersion] {
190 if clc.Name == implicitRootLib {
191 return
192 }
193 }
194
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000195 for sdkVer, otherClcs := range otherClcMap {
196 for _, otherClc := range otherClcs {
197 alreadyHave := false
198 for _, clc := range clcMap[sdkVer] {
199 if clc.Name == otherClc.Name {
200 alreadyHave = true
201 break
202 }
203 }
204 if !alreadyHave {
205 clcMap[sdkVer] = append(clcMap[sdkVer], otherClc)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100206 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100207 }
208 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100209}
210
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100211// List of libraries in the unconditional class loader context, excluding dependencies of shared
212// libraries. These libraries should be in the <uses-library> tags in the manifest. Some of them may
213// be present in the original manifest, others are added by the manifest_fixer.
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000214func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) {
215 if clcMap != nil {
216 // compatibility libraries (those in conditional context) are not added to <uses-library> tags
217 ulibs = usesLibsRec(clcMap[AnySdkVersion])
218 ulibs = android.FirstUniqueStrings(ulibs)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100219 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000220 return ulibs
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100221}
222
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000223func usesLibsRec(clcs []*ClassLoaderContext) (ulibs []string) {
224 for _, clc := range clcs {
225 ulibs = append(ulibs, clc.Name)
Ulya Trafimovicha8c28e22020-10-06 17:24:19 +0100226 // <uses-library> tags in the manifest should not include dependencies of shared libraries,
227 // because PackageManager already tracks all such dependencies and automatically adds their
228 // class loader contexts as subcontext of the shared library.
229 if !clc.IsSharedLibrary {
230 ulibs = append(ulibs, usesLibsRec(clc.Subcontexts)...)
231 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100232 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000233 return ulibs
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100234}
235
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100236// Now that the full unconditional context is known, reconstruct conditional context.
237// Apply filters for individual libraries, mirroring what the PackageManager does when it
238// constructs class loader context on device.
239//
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000240// TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps.
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100241//
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000242func fixClassLoaderContext(clcMap ClassLoaderContextMap) {
243 usesLibs := clcMap.UsesLibs()
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100244
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000245 for sdkVer, clcs := range clcMap {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100246 if sdkVer == AnySdkVersion {
247 continue
248 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000249 fixedClcs := []*ClassLoaderContext{}
250 for _, clc := range clcs {
251 if android.InList(clc.Name, usesLibs) {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100252 // skip compatibility libraries that are already included in unconditional context
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000253 } else if clc.Name == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100254 // android.test.mock is only needed as a compatibility library (in conditional class
255 // loader context) if android.test.runner is used, otherwise skip it
256 } else {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000257 fixedClcs = append(fixedClcs, clc)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100258 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000259 clcMap[sdkVer] = fixedClcs
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100260 }
261 }
262}
263
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000264// Return true if all build/install library paths are valid (including recursive subcontexts),
265// otherwise return false. A build path is valid if it's not nil. An install path is valid if it's
266// not equal to a special "error" value.
267func validateClassLoaderContext(clcMap ClassLoaderContextMap) (bool, error) {
268 for sdkVer, clcs := range clcMap {
269 if valid, err := validateClassLoaderContextRec(sdkVer, clcs); !valid || err != nil {
270 return valid, err
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100271 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000272 }
273 return true, nil
274}
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100275
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000276func validateClassLoaderContextRec(sdkVer int, clcs []*ClassLoaderContext) (bool, error) {
277 for _, clc := range clcs {
278 if clc.Host == nil || clc.Device == UnknownInstallLibraryPath {
279 if sdkVer == AnySdkVersion {
280 // Return error if dexpreopt doesn't know paths to one of the <uses-library>
281 // dependencies. In the future we may need to relax this and just disable dexpreopt.
282 return false, fmt.Errorf("invalid path for <uses-library> \"%s\"", clc.Name)
283 } else {
284 // No error for compatibility libraries, as Soong doesn't know if they are needed
285 // (this depends on the targetSdkVersion in the manifest), but the CLC is invalid.
286 return false, nil
287 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100288 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000289 if valid, err := validateClassLoaderContextRec(sdkVer, clc.Subcontexts); !valid || err != nil {
290 return valid, err
291 }
292 }
293 return true, nil
294}
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100295
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000296// Return the class loader context as a string, and a slice of build paths for all dependencies.
297// Perform a depth-first preorder traversal of the class loader context tree for each SDK version.
298// Return the resulting string and a slice of on-host build paths to all library dependencies.
299func ComputeClassLoaderContext(clcMap ClassLoaderContextMap) (clcStr string, paths android.Paths) {
300 for _, sdkVer := range android.SortedIntKeys(clcMap) { // determinisitc traversal order
301 sdkVerStr := fmt.Sprintf("%d", sdkVer)
302 if sdkVer == AnySdkVersion {
303 sdkVerStr = "any" // a special keyword that means any SDK version
304 }
305 hostClc, targetClc, hostPaths := computeClassLoaderContextRec(clcMap[sdkVer])
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100306 if hostPaths != nil {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000307 clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, hostClc)
308 clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, targetClc)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100309 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000310 paths = append(paths, hostPaths...)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100311 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000312 return clcStr, android.FirstUniquePaths(paths)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100313}
314
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000315func computeClassLoaderContextRec(clcs []*ClassLoaderContext) (string, string, android.Paths) {
316 var paths android.Paths
317 var clcsHost, clcsTarget []string
318
319 for _, clc := range clcs {
320 subClcHost, subClcTarget, subPaths := computeClassLoaderContextRec(clc.Subcontexts)
321 if subPaths != nil {
322 subClcHost = "{" + subClcHost + "}"
323 subClcTarget = "{" + subClcTarget + "}"
324 }
325
326 clcsHost = append(clcsHost, "PCL["+clc.Host.String()+"]"+subClcHost)
327 clcsTarget = append(clcsTarget, "PCL["+clc.Device+"]"+subClcTarget)
328
329 paths = append(paths, clc.Host)
330 paths = append(paths, subPaths...)
331 }
332
333 clcHost := strings.Join(clcsHost, "#")
334 clcTarget := strings.Join(clcsTarget, "#")
335
336 return clcHost, clcTarget, paths
337}
338
339// Paths to a <uses-library> on host and on device.
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100340type jsonLibraryPath struct {
341 Host string
342 Device string
343}
344
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000345// Class loader contexts that come from Make (via JSON dexpreopt.config) files have simpler
346// structure than Soong class loader contexts: they are flat maps from a <uses-library> name to its
347// on-host and on-device paths. There are no nested subcontexts. It is a limitation of the current
348// Make implementation.
349type jsonClassLoaderContext map[string]jsonLibraryPath
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100350
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000351// A map from SDK version (represented with a JSON string) to JSON class loader context.
352type jsonClassLoaderContextMap map[string]jsonClassLoaderContext
353
354// Convert JSON class loader context map to ClassLoaderContextMap.
355func fromJsonClassLoaderContext(ctx android.PathContext, jClcMap jsonClassLoaderContextMap) ClassLoaderContextMap {
356 clcMap := make(ClassLoaderContextMap)
357 for sdkVerStr, clc := range jClcMap {
358 sdkVer, ok := strconv.Atoi(sdkVerStr)
359 if ok != nil {
360 if sdkVerStr == "any" {
361 sdkVer = AnySdkVersion
362 } else {
363 android.ReportPathErrorf(ctx, "failed to parse SDK version in dexpreopt.config: '%s'", sdkVerStr)
364 }
365 }
366 for lib, path := range clc {
367 clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
368 Name: lib,
369 Host: constructPath(ctx, path.Host),
370 Device: path.Device,
371 Subcontexts: nil,
372 })
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100373 }
374 }
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000375 return clcMap
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100376}