blob: 6d778123fe15a32c2a1841bd0427d70165ea395e [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"
19 "path/filepath"
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.
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
52const AnySdkVersion int = 9999 // should go last in class loader context
53
54// LibraryPath contains paths to the library DEX jar on host and on device.
55type LibraryPath struct {
56 Host android.Path
57 Device string
58}
59
60// LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar.
61type LibraryPaths map[string]*LibraryPath
62
63type classLoaderContext struct {
64 // Library names
65 Names []string
66
67 // The class loader context using paths in the build.
68 Host android.Paths
69
70 // The class loader context using paths as they will be on the device.
71 Target []string
72}
73
74// A map of class loader contexts for each SDK version.
75// A map entry for "any" version contains libraries that are unconditionally added to class loader
76// context. Map entries for existing versions contains libraries that were in the default classpath
77// until that API version, and should be added to class loader context if and only if the
78// targetSdkVersion in the manifest or APK is less than that API version.
79type classLoaderContextMap map[int]*classLoaderContext
80
81// Add a new library path to the map, unless a path for this library already exists.
82// If necessary, check that the build and install paths exist.
Ulya Trafimovich69612672020-10-20 17:41:54 +010083func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleInstallPathContext, lib string,
84 hostPath, installPath android.Path, strict bool) 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 Trafimovich69612672020-10-20 17:41:54 +010092 return fmt.Errorf("unknown build path to <uses-library> '%s'", lib)
Ulya Trafimovicheb268862020-10-20 15:16:38 +010093 }
94
95 if installPath == nil {
96 if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
97 // Assume that compatibility libraries are installed in /system/framework.
98 installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
99 } else if strict {
Ulya Trafimovich69612672020-10-20 17:41:54 +0100100 return fmt.Errorf("unknown install path to <uses-library> '%s'", lib)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100101 }
102 }
103
104 // Add a library only if the build and install path to it is known.
105 if _, present := libPaths[lib]; !present {
106 var devicePath string
107 if installPath != nil {
108 devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
109 } else {
110 // For some stub libraries the only known thing is the name of their implementation
111 // library, but the library itself is unavailable (missing or part of a prebuilt). In
112 // such cases we still need to add the library to <uses-library> tags in the manifest,
113 // but we cannot use if for dexpreopt.
114 devicePath = UnknownInstallLibraryPath
115 }
116 libPaths[lib] = &LibraryPath{hostPath, devicePath}
117 }
Ulya Trafimovich69612672020-10-20 17:41:54 +0100118 return nil
119}
120
121// Wrapper around addLibraryPath that does error reporting.
122func (libPaths LibraryPaths) addLibraryPathOrReportError(ctx android.ModuleInstallPathContext, lib string,
123 hostPath, installPath android.Path, strict bool) {
124
125 err := libPaths.addLibraryPath(ctx, lib, hostPath, installPath, strict)
126 if err != nil {
127 android.ReportPathErrorf(ctx, err.Error())
128 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100129}
130
131// Add a new library path to the map. Enforce checks that the library paths exist.
Ulya Trafimovich69612672020-10-20 17:41:54 +0100132func (libPaths LibraryPaths) AddLibraryPath(ctx android.ModuleInstallPathContext, lib string, hostPath, installPath android.Path) {
133 libPaths.addLibraryPathOrReportError(ctx, lib, hostPath, installPath, true)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100134}
135
136// Add a new library path to the map, if the library exists (name is not nil).
137// Don't enforce checks that the library paths exist. Some libraries may be missing from the build,
138// but their names still need to be added to <uses-library> tags in the manifest.
Ulya Trafimovich69612672020-10-20 17:41:54 +0100139func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.ModuleInstallPathContext, lib *string, hostPath, installPath android.Path) {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100140 if lib != nil {
Ulya Trafimovich69612672020-10-20 17:41:54 +0100141 libPaths.addLibraryPathOrReportError(ctx, *lib, hostPath, installPath, false)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100142 }
143}
144
145// Add library paths from the second map to the first map (do not override existing entries).
146func (libPaths LibraryPaths) AddLibraryPaths(otherPaths LibraryPaths) {
147 for lib, path := range otherPaths {
148 if _, present := libPaths[lib]; !present {
149 libPaths[lib] = path
150 }
151 }
152}
153
154func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
155 if _, ok := m[sdkVer]; !ok {
156 m[sdkVer] = &classLoaderContext{}
157 }
158 return m[sdkVer]
159}
160
161func (clc *classLoaderContext) addLib(lib string, hostPath android.Path, targetPath string) {
162 clc.Names = append(clc.Names, lib)
163 clc.Host = append(clc.Host, hostPath)
164 clc.Target = append(clc.Target, targetPath)
165}
166
Ulya Trafimovich69612672020-10-20 17:41:54 +0100167func (m classLoaderContextMap) addLibs(ctx android.PathContext, sdkVer int, module *ModuleConfig,
168 libs ...string) (bool, error) {
169
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100170 clc := m.getValue(sdkVer)
171 for _, lib := range libs {
172 if p, ok := module.LibraryPaths[lib]; ok && p.Host != nil && p.Device != UnknownInstallLibraryPath {
173 clc.addLib(lib, p.Host, p.Device)
174 } else {
175 if sdkVer == AnySdkVersion {
176 // Fail the build if dexpreopt doesn't know paths to one of the <uses-library>
177 // dependencies. In the future we may need to relax this and just disable dexpreopt.
Ulya Trafimovich69612672020-10-20 17:41:54 +0100178 return false, fmt.Errorf("dexpreopt cannot find path for <uses-library> '%s'", lib)
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100179 } else {
180 // No error for compatibility libraries, as Soong doesn't know if they are needed
181 // (this depends on the targetSdkVersion in the manifest).
Ulya Trafimovich69612672020-10-20 17:41:54 +0100182 return false, nil
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100183 }
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100184 }
185 }
Ulya Trafimovich69612672020-10-20 17:41:54 +0100186 return true, nil
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100187}
188
189func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
190 clc := m.getValue(sdkVer)
191 for _, lib := range libs {
192 clc.addLib(lib, SystemServerDexJarHostPath(ctx, lib), filepath.Join("/system/framework", lib+".jar"))
193 }
194}
195
196func (m classLoaderContextMap) usesLibs() []string {
197 if clc, ok := m[AnySdkVersion]; ok {
198 return clc.Names
199 }
200 return nil
201}
202
203// genClassLoaderContext generates host and target class loader context to be passed to the dex2oat
204// command for the dexpreopted module. There are three possible cases:
205//
206// 1. System server jars. They have a special class loader context that includes other system
207// server jars.
208//
209// 2. Library jars or APKs which have precise list of their <uses-library> libs. Their class loader
210// context includes build and on-device paths to these libs. In some cases it may happen that
211// the path to a <uses-library> is unknown (e.g. the dexpreopted module may depend on stubs
212// library, whose implementation library is missing from the build altogether). In such case
213// dexpreopting with the <uses-library> is impossible, and dexpreopting without it is pointless,
214// as the runtime classpath won't match and the dexpreopted code will be discarded. Therefore in
215// such cases the function returns nil, which disables dexpreopt.
216//
217// 3. All other library jars or APKs for which the exact <uses-library> list is unknown. They use
218// the unsafe &-classpath workaround that means empty class loader context and absence of runtime
219// check that the class loader context provided by the PackageManager agrees with the stored
220// class loader context recorded in the .odex file.
221//
Ulya Trafimovich69612672020-10-20 17:41:54 +0100222func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) (*classLoaderContextMap, error) {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100223 classLoaderContexts := make(classLoaderContextMap)
224 systemServerJars := NonUpdatableSystemServerJars(ctx, global)
225
226 if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
227 // System server jars should be dexpreopted together: class loader context of each jar
228 // should include all preceding jars on the system server classpath.
229 classLoaderContexts.addSystemServerLibs(AnySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
230
231 } else if module.EnforceUsesLibraries {
232 // Unconditional class loader context.
233 usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
Ulya Trafimovich69612672020-10-20 17:41:54 +0100234 if ok, err := classLoaderContexts.addLibs(ctx, AnySdkVersion, module, usesLibs...); !ok {
235 return nil, err
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100236 }
237
238 // Conditional class loader context for API version < 28.
239 const httpLegacy = "org.apache.http.legacy"
Ulya Trafimovich69612672020-10-20 17:41:54 +0100240 if ok, err := classLoaderContexts.addLibs(ctx, 28, module, httpLegacy); !ok {
241 return nil, err
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100242 }
243
244 // Conditional class loader context for API version < 29.
245 usesLibs29 := []string{
246 "android.hidl.base-V1.0-java",
247 "android.hidl.manager-V1.0-java",
248 }
Ulya Trafimovich69612672020-10-20 17:41:54 +0100249 if ok, err := classLoaderContexts.addLibs(ctx, 29, module, usesLibs29...); !ok {
250 return nil, err
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100251 }
252
253 // Conditional class loader context for API version < 30.
Ulya Trafimovich69612672020-10-20 17:41:54 +0100254 if ok, err := classLoaderContexts.addLibs(ctx, 30, module, OptionalCompatUsesLibs30...); !ok {
255 return nil, err
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100256 }
257
258 } else {
259 // Pass special class loader context to skip the classpath and collision check.
260 // This will get removed once LOCAL_USES_LIBRARIES is enforced.
261 // Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
262 // to the &.
263 }
264
265 fixConditionalClassLoaderContext(classLoaderContexts)
266
Ulya Trafimovich69612672020-10-20 17:41:54 +0100267 return &classLoaderContexts, nil
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100268}
269
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100270// Now that the full unconditional context is known, reconstruct conditional context.
271// Apply filters for individual libraries, mirroring what the PackageManager does when it
272// constructs class loader context on device.
273//
274// TODO(b/132357300):
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100275// - remove android.hidl.manager and android.hidl.base unless the app is a system app.
276//
277func fixConditionalClassLoaderContext(clcMap classLoaderContextMap) {
278 usesLibs := clcMap.usesLibs()
279
280 for sdkVer, clc := range clcMap {
281 if sdkVer == AnySdkVersion {
282 continue
283 }
284 clcMap[sdkVer] = &classLoaderContext{}
285 for i, lib := range clc.Names {
286 if android.InList(lib, usesLibs) {
287 // skip compatibility libraries that are already included in unconditional context
288 } else if lib == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
289 // android.test.mock is only needed as a compatibility library (in conditional class
290 // loader context) if android.test.runner is used, otherwise skip it
291 } else {
292 clcMap[sdkVer].addLib(lib, clc.Host[i], clc.Target[i])
293 }
294 }
295 }
296}
297
298// Return the class loader context as a string and a slice of build paths for all dependencies.
299func computeClassLoaderContext(ctx android.PathContext, clcMap classLoaderContextMap) (clcStr string, paths android.Paths) {
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100300 for _, ver := range android.SortedIntKeys(clcMap) {
301 clc := clcMap.getValue(ver)
302
303 clcLen := len(clc.Names)
304 if clcLen != len(clc.Host) || clcLen != len(clc.Target) {
305 android.ReportPathErrorf(ctx, "ill-formed class loader context")
306 }
307
308 var hostClc, targetClc []string
309 var hostPaths android.Paths
310
311 for i := 0; i < clcLen; i++ {
312 hostStr := "PCL[" + clc.Host[i].String() + "]"
313 targetStr := "PCL[" + clc.Target[i] + "]"
314
Ulya Trafimovicheb268862020-10-20 15:16:38 +0100315 hostClc = append(hostClc, hostStr)
316 targetClc = append(targetClc, targetStr)
317 hostPaths = append(hostPaths, clc.Host[i])
318 }
319
320 if hostPaths != nil {
321 sdkVerStr := fmt.Sprintf("%d", ver)
322 if ver == AnySdkVersion {
323 sdkVerStr = "any" // a special keyword that means any SDK version
324 }
325 clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, strings.Join(hostClc, "#"))
326 clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, strings.Join(targetClc, "#"))
327 paths = append(paths, hostPaths...)
328 }
329 }
330
331 return clcStr, paths
332}
333
334type jsonLibraryPath struct {
335 Host string
336 Device string
337}
338
339type jsonLibraryPaths map[string]jsonLibraryPath
340
341// convert JSON map of library paths to LibraryPaths
342func constructLibraryPaths(ctx android.PathContext, paths jsonLibraryPaths) LibraryPaths {
343 m := LibraryPaths{}
344 for lib, path := range paths {
345 m[lib] = &LibraryPath{
346 constructPath(ctx, path.Host),
347 path.Device,
348 }
349 }
350 return m
351}