Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License |
| 15 | */ |
| 16 | |
| 17 | package com.android.server.pm.dex; |
| 18 | |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 19 | import android.content.pm.ApplicationInfo; |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 20 | import android.content.pm.SharedLibraryInfo; |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 21 | import android.util.Slog; |
| 22 | import android.util.SparseArray; |
| 23 | |
| 24 | import com.android.internal.os.ClassLoaderFactory; |
| 25 | |
| 26 | import java.io.File; |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 27 | import java.util.List; |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 28 | |
| 29 | public final class DexoptUtils { |
| 30 | private static final String TAG = "DexoptUtils"; |
| 31 | |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 32 | // Shared libraries have more or less followed PCL behavior due to the way |
| 33 | // they were added to the classpath pre Q. |
| 34 | private static final String SHARED_LIBRARY_LOADER_TYPE = |
| 35 | ClassLoaderFactory.getPathClassLoaderName(); |
| 36 | |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 37 | private DexoptUtils() {} |
| 38 | |
| 39 | /** |
| 40 | * Creates the class loader context dependencies for each of the application code paths. |
| 41 | * The returned array contains the class loader contexts that needs to be passed to dexopt in |
Calin Juravle | da09815 | 2017-09-01 17:30:01 -0700 | [diff] [blame] | 42 | * order to ensure correct optimizations. "Code" paths with no actual code, as specified by |
| 43 | * {@param pathsWithCode}, are ignored and will have null as their context in the returned array |
| 44 | * (configuration splits are an example of paths without code). |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 45 | * |
| 46 | * A class loader context describes how the class loader chain should be built by dex2oat |
| 47 | * in order to ensure that classes are resolved during compilation as they would be resolved |
| 48 | * at runtime. The context will be encoded in the compiled code. If at runtime the dex file is |
| 49 | * loaded in a different context (with a different set of class loaders or a different |
| 50 | * classpath), the compiled code will be rejected. |
| 51 | * |
| 52 | * Note that the class loader context only includes dependencies and not the code path itself. |
| 53 | * The contexts are created based on the application split dependency list and |
| 54 | * the provided shared libraries. |
| 55 | * |
| 56 | * All the code paths encoded in the context will be relative to the base directory. This |
| 57 | * enables stage compilation where compiler artifacts may be moved around. |
| 58 | * |
| 59 | * The result is indexed as follows: |
| 60 | * - index 0 contains the context for the base apk |
| 61 | * - index 1 to n contain the context for the splits in the order determined by |
| 62 | * {@code info.getSplitCodePaths()} |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 63 | * |
| 64 | * IMPORTANT: keep this logic in sync with the loading code in {@link android.app.LoadedApk} |
| 65 | * and pay attention to the way the classpath is created for the non isolated mode in: |
| 66 | * {@link android.app.LoadedApk#makePaths( |
| 67 | * android.app.ActivityThread, boolean, ApplicationInfo, List, List)}. |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 68 | */ |
Calin Juravle | da09815 | 2017-09-01 17:30:01 -0700 | [diff] [blame] | 69 | public static String[] getClassLoaderContexts(ApplicationInfo info, |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 70 | List<SharedLibraryInfo> sharedLibraries, boolean[] pathsWithCode) { |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 71 | // The base class loader context contains only the shared library. |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 72 | String sharedLibrariesContext = ""; |
| 73 | if (sharedLibraries != null) { |
| 74 | sharedLibrariesContext = encodeSharedLibraries(sharedLibraries); |
| 75 | } |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 76 | |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 77 | String baseApkContextClassLoader = encodeClassLoader( |
| 78 | "", info.classLoaderName, sharedLibrariesContext); |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 79 | if (info.getSplitCodePaths() == null) { |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 80 | // The application has no splits. |
| 81 | return new String[] {baseApkContextClassLoader}; |
| 82 | } |
| 83 | |
| 84 | // The application has splits. Compute their class loader contexts. |
| 85 | |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 86 | // First, cache the relative paths of the splits and do some sanity checks |
| 87 | String[] splitRelativeCodePaths = getSplitRelativeCodePaths(info); |
| 88 | |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 89 | // The splits have an implicit dependency on the base apk. |
| 90 | // This means that we have to add the base apk file in addition to the shared libraries. |
| 91 | String baseApkName = new File(info.getBaseCodePath()).getName(); |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 92 | String baseClassPath = baseApkName; |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 93 | |
| 94 | // The result is stored in classLoaderContexts. |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 95 | // Index 0 is the class loader context for the base apk. |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 96 | // Index `i` is the class loader context encoding for split `i`. |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 97 | String[] classLoaderContexts = new String[/*base apk*/ 1 + splitRelativeCodePaths.length]; |
Calin Juravle | da09815 | 2017-09-01 17:30:01 -0700 | [diff] [blame] | 98 | classLoaderContexts[0] = pathsWithCode[0] ? baseApkContextClassLoader : null; |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 99 | |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 100 | if (!info.requestsIsolatedSplitLoading() || info.splitDependencies == null) { |
| 101 | // If the app didn't request for the splits to be loaded in isolation or if it does not |
| 102 | // declare inter-split dependencies, then all the splits will be loaded in the base |
| 103 | // apk class loader (in the order of their definition). |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 104 | String classpath = baseClassPath; |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 105 | for (int i = 1; i < classLoaderContexts.length; i++) { |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 106 | if (pathsWithCode[i]) { |
| 107 | classLoaderContexts[i] = encodeClassLoader( |
| 108 | classpath, info.classLoaderName, sharedLibrariesContext); |
| 109 | } else { |
| 110 | classLoaderContexts[i] = null; |
| 111 | } |
Calin Juravle | da09815 | 2017-09-01 17:30:01 -0700 | [diff] [blame] | 112 | // Note that the splits with no code are not removed from the classpath computation. |
| 113 | // i.e. split_n might get the split_n-1 in its classpath dependency even |
| 114 | // if split_n-1 has no code. |
| 115 | // The splits with no code do not matter for the runtime which ignores |
| 116 | // apks without code when doing the classpath checks. As such we could actually |
| 117 | // filter them but we don't do it in order to keep consistency with how the apps |
| 118 | // are loaded. |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 119 | classpath = encodeClasspath(classpath, splitRelativeCodePaths[i - 1]); |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 120 | } |
| 121 | } else { |
| 122 | // In case of inter-split dependencies, we need to walk the dependency chain of each |
| 123 | // split. We do this recursively and store intermediate results in classLoaderContexts. |
| 124 | |
| 125 | // First, look at the split class loaders and cache their individual contexts (i.e. |
| 126 | // the class loader + the name of the split). This is an optimization to avoid |
| 127 | // re-computing them during the recursive call. |
| 128 | // The cache is stored in splitClassLoaderEncodingCache. The difference between this and |
| 129 | // classLoaderContexts is that the later contains the full chain of class loaders for |
| 130 | // a given split while splitClassLoaderEncodingCache only contains a single class loader |
| 131 | // encoding. |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 132 | String[] splitClassLoaderEncodingCache = new String[splitRelativeCodePaths.length]; |
| 133 | for (int i = 0; i < splitRelativeCodePaths.length; i++) { |
| 134 | splitClassLoaderEncodingCache[i] = encodeClassLoader(splitRelativeCodePaths[i], |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 135 | info.splitClassLoaderNames[i]); |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 136 | } |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 137 | String splitDependencyOnBase = encodeClassLoader( |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 138 | baseClassPath, info.classLoaderName); |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 139 | SparseArray<int[]> splitDependencies = info.splitDependencies; |
Calin Juravle | da09815 | 2017-09-01 17:30:01 -0700 | [diff] [blame] | 140 | |
| 141 | // Note that not all splits have dependencies (e.g. configuration splits) |
| 142 | // The splits without dependencies will have classLoaderContexts[config_split_index] |
| 143 | // set to null after this step. |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 144 | for (int i = 1; i < splitDependencies.size(); i++) { |
Calin Juravle | da09815 | 2017-09-01 17:30:01 -0700 | [diff] [blame] | 145 | int splitIndex = splitDependencies.keyAt(i); |
| 146 | if (pathsWithCode[splitIndex]) { |
| 147 | // Compute the class loader context only for the splits with code. |
| 148 | getParentDependencies(splitIndex, splitClassLoaderEncodingCache, |
| 149 | splitDependencies, classLoaderContexts, splitDependencyOnBase); |
| 150 | } |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 151 | } |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 152 | |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 153 | // At this point classLoaderContexts contains only the parent dependencies. |
| 154 | // We also need to add the class loader of the current split which should |
| 155 | // come first in the context. |
| 156 | for (int i = 1; i < classLoaderContexts.length; i++) { |
| 157 | String splitClassLoader = encodeClassLoader("", info.splitClassLoaderNames[i - 1]); |
Calin Juravle | da09815 | 2017-09-01 17:30:01 -0700 | [diff] [blame] | 158 | if (pathsWithCode[i]) { |
| 159 | // If classLoaderContexts[i] is null it means that the split does not have |
| 160 | // any dependency. In this case its context equals its declared class loader. |
| 161 | classLoaderContexts[i] = classLoaderContexts[i] == null |
| 162 | ? splitClassLoader |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 163 | : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i]) |
| 164 | + sharedLibrariesContext; |
Calin Juravle | da09815 | 2017-09-01 17:30:01 -0700 | [diff] [blame] | 165 | } else { |
| 166 | // This is a split without code, it has no dependency and it is not compiled. |
| 167 | // Its context will be null. |
| 168 | classLoaderContexts[i] = null; |
| 169 | } |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 170 | } |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 171 | } |
| 172 | |
| 173 | return classLoaderContexts; |
| 174 | } |
| 175 | |
| 176 | /** |
Nicolas Geoffray | 653356f | 2019-02-26 23:24:34 +0000 | [diff] [blame] | 177 | * Creates the class loader context for the given shared library. |
| 178 | */ |
| 179 | public static String getClassLoaderContext(SharedLibraryInfo info) { |
| 180 | String sharedLibrariesContext = ""; |
| 181 | if (info.getDependencies() != null) { |
| 182 | sharedLibrariesContext = encodeSharedLibraries(info.getDependencies()); |
| 183 | } |
| 184 | return encodeClassLoader( |
| 185 | "", SHARED_LIBRARY_LOADER_TYPE, sharedLibrariesContext); |
| 186 | } |
| 187 | |
| 188 | /** |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 189 | * Recursive method to generate the class loader context dependencies for the split with the |
| 190 | * given index. {@param classLoaderContexts} acts as an accumulator. Upton return |
| 191 | * {@code classLoaderContexts[index]} will contain the split dependency. |
| 192 | * During computation, the method may resolve the dependencies of other splits as it traverses |
| 193 | * the entire parent chain. The result will also be stored in {@param classLoaderContexts}. |
| 194 | * |
| 195 | * Note that {@code index 0} denotes the base apk and it is special handled. When the |
| 196 | * recursive call hits {@code index 0} the method returns {@code splitDependencyOnBase}. |
| 197 | * {@code classLoaderContexts[0]} is not modified in this method. |
| 198 | * |
| 199 | * @param index the index of the split (Note that index 0 denotes the base apk) |
| 200 | * @param splitClassLoaderEncodingCache the class loader encoding for the individual splits. |
| 201 | * It contains only the split class loader and not the the base. The split |
| 202 | * with {@code index} has its context at {@code splitClassLoaderEncodingCache[index - 1]}. |
| 203 | * @param splitDependencies the dependencies for all splits. Note that in this array index 0 |
| 204 | * is the base and splits start from index 1. |
| 205 | * @param classLoaderContexts the result accumulator. index 0 is the base and never set. Splits |
| 206 | * start at index 1. |
| 207 | * @param splitDependencyOnBase the encoding of the implicit split dependency on base. |
| 208 | */ |
| 209 | private static String getParentDependencies(int index, String[] splitClassLoaderEncodingCache, |
| 210 | SparseArray<int[]> splitDependencies, String[] classLoaderContexts, |
| 211 | String splitDependencyOnBase) { |
| 212 | // If we hit the base apk return its custom dependency list which is |
| 213 | // sharedLibraries + base.apk |
| 214 | if (index == 0) { |
| 215 | return splitDependencyOnBase; |
| 216 | } |
| 217 | // Return the result if we've computed the splitDependencies for this index already. |
| 218 | if (classLoaderContexts[index] != null) { |
| 219 | return classLoaderContexts[index]; |
| 220 | } |
| 221 | // Get the splitDependencies for the parent of this index and append its path to it. |
| 222 | int parent = splitDependencies.get(index)[0]; |
| 223 | String parentDependencies = getParentDependencies(parent, splitClassLoaderEncodingCache, |
| 224 | splitDependencies, classLoaderContexts, splitDependencyOnBase); |
| 225 | |
| 226 | // The split context is: `parent context + parent dependencies context`. |
| 227 | String splitContext = (parent == 0) ? |
| 228 | parentDependencies : |
| 229 | encodeClassLoaderChain(splitClassLoaderEncodingCache[parent - 1], parentDependencies); |
| 230 | classLoaderContexts[index] = splitContext; |
| 231 | return splitContext; |
| 232 | } |
| 233 | |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 234 | private static String encodeSharedLibrary(SharedLibraryInfo sharedLibrary) { |
| 235 | List<String> paths = sharedLibrary.getAllCodePaths(); |
| 236 | String classLoaderSpec = encodeClassLoader( |
| 237 | encodeClasspath(paths.toArray(new String[paths.size()])), |
| 238 | SHARED_LIBRARY_LOADER_TYPE); |
| 239 | if (sharedLibrary.getDependencies() != null) { |
| 240 | classLoaderSpec += encodeSharedLibraries(sharedLibrary.getDependencies()); |
| 241 | } |
| 242 | return classLoaderSpec; |
| 243 | } |
| 244 | |
| 245 | private static String encodeSharedLibraries(List<SharedLibraryInfo> sharedLibraries) { |
| 246 | String sharedLibrariesContext = "{"; |
| 247 | boolean first = true; |
| 248 | for (SharedLibraryInfo info : sharedLibraries) { |
| 249 | if (!first) { |
| 250 | sharedLibrariesContext += "#"; |
| 251 | } |
| 252 | first = false; |
| 253 | sharedLibrariesContext += encodeSharedLibrary(info); |
| 254 | } |
| 255 | sharedLibrariesContext += "}"; |
| 256 | return sharedLibrariesContext; |
| 257 | } |
| 258 | |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 259 | /** |
| 260 | * Encodes the shared libraries classpathElements in a format accepted by dexopt. |
| 261 | * NOTE: Keep this in sync with the dexopt expectations! Right now that is |
| 262 | * a list separated by ':'. |
| 263 | */ |
| 264 | private static String encodeClasspath(String[] classpathElements) { |
| 265 | if (classpathElements == null || classpathElements.length == 0) { |
| 266 | return ""; |
| 267 | } |
| 268 | StringBuilder sb = new StringBuilder(); |
| 269 | for (String element : classpathElements) { |
| 270 | if (sb.length() != 0) { |
| 271 | sb.append(":"); |
| 272 | } |
| 273 | sb.append(element); |
| 274 | } |
| 275 | return sb.toString(); |
| 276 | } |
| 277 | |
| 278 | /** |
| 279 | * Adds an element to the encoding of an existing classpath. |
| 280 | * {@see PackageDexOptimizer.encodeClasspath(String[])} |
| 281 | */ |
| 282 | private static String encodeClasspath(String classpath, String newElement) { |
| 283 | return classpath.isEmpty() ? newElement : (classpath + ":" + newElement); |
| 284 | } |
| 285 | |
| 286 | /** |
| 287 | * Encodes a single class loader dependency starting from {@param path} and |
| 288 | * {@param classLoaderName}. |
| 289 | * NOTE: Keep this in sync with the dexopt expectations! Right now that is either "PCL[path]" |
| 290 | * for a PathClassLoader or "DLC[path]" for a DelegateLastClassLoader. |
| 291 | */ |
Shubham Ajmera | 727aaa3 | 2017-09-05 10:20:41 -0700 | [diff] [blame] | 292 | /*package*/ static String encodeClassLoader(String classpath, String classLoaderName) { |
Nicolas Geoffray | bab4c2c | 2018-12-20 09:47:00 +0000 | [diff] [blame] | 293 | classpath.getClass(); // Throw NPE if classpath is null |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 294 | String classLoaderDexoptEncoding = classLoaderName; |
| 295 | if (ClassLoaderFactory.isPathClassLoaderName(classLoaderName)) { |
| 296 | classLoaderDexoptEncoding = "PCL"; |
| 297 | } else if (ClassLoaderFactory.isDelegateLastClassLoaderName(classLoaderName)) { |
| 298 | classLoaderDexoptEncoding = "DLC"; |
| 299 | } else { |
| 300 | Slog.wtf(TAG, "Unsupported classLoaderName: " + classLoaderName); |
| 301 | } |
| 302 | return classLoaderDexoptEncoding + "[" + classpath + "]"; |
| 303 | } |
| 304 | |
| 305 | /** |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 306 | * Same as above, but appends {@param sharedLibraries} to the result. |
| 307 | */ |
| 308 | private static String encodeClassLoader(String classpath, String classLoaderName, |
| 309 | String sharedLibraries) { |
| 310 | return encodeClassLoader(classpath, classLoaderName) + sharedLibraries; |
| 311 | } |
| 312 | |
| 313 | /** |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 314 | * Links to dependencies together in a format accepted by dexopt. |
Shubham Ajmera | 727aaa3 | 2017-09-05 10:20:41 -0700 | [diff] [blame] | 315 | * For the special case when either of cl1 or cl2 equals |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 316 | * NOTE: Keep this in sync with the dexopt expectations! Right now that is a list of split |
| 317 | * dependencies {@see encodeClassLoader} separated by ';'. |
| 318 | */ |
Shubham Ajmera | 727aaa3 | 2017-09-05 10:20:41 -0700 | [diff] [blame] | 319 | /*package*/ static String encodeClassLoaderChain(String cl1, String cl2) { |
Calin Juravle | f1ff36f | 2017-07-22 12:33:41 -0700 | [diff] [blame] | 320 | if (cl1.isEmpty()) return cl2; |
| 321 | if (cl2.isEmpty()) return cl1; |
| 322 | return cl1 + ";" + cl2; |
| 323 | } |
| 324 | |
| 325 | /** |
| 326 | * Compute the class loader context for the dex files present in the classpath of the first |
| 327 | * class loader from the given list (referred in the code as the {@code loadingClassLoader}). |
| 328 | * Each dex files gets its own class loader context in the returned array. |
| 329 | * |
| 330 | * Example: |
| 331 | * If classLoadersNames = {"dalvik.system.DelegateLastClassLoader", |
| 332 | * "dalvik.system.PathClassLoader"} and classPaths = {"foo.dex:bar.dex", "other.dex"} |
| 333 | * The output will be |
| 334 | * {"DLC[];PCL[other.dex]", "DLC[foo.dex];PCL[other.dex]"} |
| 335 | * with "DLC[];PCL[other.dex]" being the context for "foo.dex" |
| 336 | * and "DLC[foo.dex];PCL[other.dex]" the context for "bar.dex". |
| 337 | * |
| 338 | * If any of the class loaders names is unsupported the method will return null. |
| 339 | * |
| 340 | * The argument lists must be non empty and of the same size. |
| 341 | * |
| 342 | * @param classLoadersNames the names of the class loaders present in the loading chain. The |
| 343 | * list encodes the class loader chain in the natural order. The first class loader has |
| 344 | * the second one as its parent and so on. |
| 345 | * @param classPaths the class paths for the elements of {@param classLoadersNames}. The |
| 346 | * the first element corresponds to the first class loader and so on. A classpath is |
| 347 | * represented as a list of dex files separated by {@code File.pathSeparator}. |
| 348 | * The return context will be for the dex files found in the first class path. |
| 349 | */ |
| 350 | /*package*/ static String[] processContextForDexLoad(List<String> classLoadersNames, |
| 351 | List<String> classPaths) { |
| 352 | if (classLoadersNames.size() != classPaths.size()) { |
| 353 | throw new IllegalArgumentException( |
| 354 | "The size of the class loader names and the dex paths do not match."); |
| 355 | } |
| 356 | if (classLoadersNames.isEmpty()) { |
| 357 | throw new IllegalArgumentException("Empty classLoadersNames"); |
| 358 | } |
| 359 | |
| 360 | // Compute the context for the parent class loaders. |
| 361 | String parentContext = ""; |
| 362 | // We know that these lists are actually ArrayLists so getting the elements by index |
| 363 | // is fine (they come over binder). Even if something changes we expect the sizes to be |
| 364 | // very small and it shouldn't matter much. |
| 365 | for (int i = 1; i < classLoadersNames.size(); i++) { |
Alan Stokes | b6c3a60 | 2018-11-02 12:10:42 +0000 | [diff] [blame] | 366 | if (!ClassLoaderFactory.isValidClassLoaderName(classLoadersNames.get(i)) |
| 367 | || classPaths.get(i) == null) { |
Calin Juravle | f1ff36f | 2017-07-22 12:33:41 -0700 | [diff] [blame] | 368 | return null; |
| 369 | } |
| 370 | String classpath = encodeClasspath(classPaths.get(i).split(File.pathSeparator)); |
| 371 | parentContext = encodeClassLoaderChain(parentContext, |
| 372 | encodeClassLoader(classpath, classLoadersNames.get(i))); |
| 373 | } |
| 374 | |
| 375 | // Now compute the class loader context for each dex file from the first classpath. |
| 376 | String loadingClassLoader = classLoadersNames.get(0); |
| 377 | if (!ClassLoaderFactory.isValidClassLoaderName(loadingClassLoader)) { |
| 378 | return null; |
| 379 | } |
| 380 | String[] loadedDexPaths = classPaths.get(0).split(File.pathSeparator); |
| 381 | String[] loadedDexPathsContext = new String[loadedDexPaths.length]; |
| 382 | String currentLoadedDexPathClasspath = ""; |
| 383 | for (int i = 0; i < loadedDexPaths.length; i++) { |
| 384 | String dexPath = loadedDexPaths[i]; |
| 385 | String currentContext = encodeClassLoader( |
| 386 | currentLoadedDexPathClasspath, loadingClassLoader); |
| 387 | loadedDexPathsContext[i] = encodeClassLoaderChain(currentContext, parentContext); |
| 388 | currentLoadedDexPathClasspath = encodeClasspath(currentLoadedDexPathClasspath, dexPath); |
| 389 | } |
| 390 | return loadedDexPathsContext; |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 391 | } |
Calin Juravle | 305aeea | 2017-07-14 16:40:07 -0700 | [diff] [blame] | 392 | |
| 393 | /** |
| 394 | * Returns the relative paths of the splits declared by the application {@code info}. |
| 395 | * Assumes that the application declares a non-null array of splits. |
| 396 | */ |
| 397 | private static String[] getSplitRelativeCodePaths(ApplicationInfo info) { |
| 398 | String baseCodePath = new File(info.getBaseCodePath()).getParent(); |
| 399 | String[] splitCodePaths = info.getSplitCodePaths(); |
| 400 | String[] splitRelativeCodePaths = new String[splitCodePaths.length]; |
| 401 | for (int i = 0; i < splitCodePaths.length; i++) { |
| 402 | File pathFile = new File(splitCodePaths[i]); |
| 403 | splitRelativeCodePaths[i] = pathFile.getName(); |
| 404 | // Sanity check that the base paths of the splits are all the same. |
| 405 | String basePath = pathFile.getParent(); |
| 406 | if (!basePath.equals(baseCodePath)) { |
| 407 | Slog.wtf(TAG, "Split paths have different base paths: " + basePath + " and " + |
| 408 | baseCodePath); |
| 409 | } |
| 410 | } |
| 411 | return splitRelativeCodePaths; |
| 412 | } |
Calin Juravle | 19da1cf | 2017-07-12 18:52:49 -0700 | [diff] [blame] | 413 | } |