1 /* 2 * Copyright (C) 2021 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 package com.android.car.ui.sharedlibrarysupport; 17 18 import androidx.annotation.NonNull; 19 import androidx.annotation.Nullable; 20 21 import dalvik.system.PathClassLoader; 22 23 import java.util.regex.Pattern; 24 25 /** 26 * This is a {@link PathClassLoader} that you can pass an additional classloaders to the 27 * constructor. That will be searched after the classes in this classloader have been searched, 28 * but before the parent classloader is searched. The first classloader in the list of 29 * classloaders is set as the parent classloader. 30 * 31 * Much of the code is copied from {@link dalvik.system.DelegateLastClassLoader}. However, 32 * note that resource loading is not implemented the same way just because we didn't need it. 33 */ 34 class AdapterClassLoader extends PathClassLoader { 35 36 @Nullable 37 private final ClassLoader mSharedLibraryClassLoader; 38 39 private static final Pattern PATTERN = Pattern.compile( 40 "^com\\.android\\.car\\.ui\\..*AdapterV[0-9]+(\\$.*)?$" 41 + "|Lambda" 42 + "|^" + Pattern.quote(OemApiUtil.class.getName()) + "$"); 43 44 private static final Pattern SHARED_LIBRARY_PATTERN = 45 Pattern.compile("^com\\.android\\.car\\.ui\\.sharedlibrary\\." 46 + "(oemapis\\..*|SharedLibraryVersionProviderImpl)$"); 47 48 /** 49 * Equivalent to calling {@link #AdapterClassLoader(String, String, ClassLoader, ClassLoader)} 50 * with {@code librarySearchPath = null, delegateResourceLoading = true}. 51 */ AdapterClassLoader(String dexPath, @Nullable ClassLoader parent, @Nullable ClassLoader additionalClassloader)52 AdapterClassLoader(String dexPath, @Nullable ClassLoader parent, 53 @Nullable ClassLoader additionalClassloader) { 54 this(dexPath, null, parent, additionalClassloader); 55 } 56 57 /** 58 * Equivalent to calling 59 * {@link #AdapterClassLoader(String, String, ClassLoader, ClassLoader, boolean)} 60 * with {@code delegateResourceLoading = true}. 61 */ AdapterClassLoader(String dexPath, String librarySearchPath, @Nullable ClassLoader parent, @Nullable ClassLoader additionalClassloader)62 AdapterClassLoader(String dexPath, String librarySearchPath, @Nullable ClassLoader parent, 63 @Nullable ClassLoader additionalClassloader) { 64 this(dexPath, librarySearchPath, parent, additionalClassloader, true); 65 } 66 67 /** 68 * See {@link dalvik.system.DelegateLastClassLoader#DelegateLastClassLoader 69 * (String, String, List<ClassLoader>, boolean)}. 70 */ AdapterClassLoader(@onNull String dexPath, @Nullable String librarySearchPath, @Nullable ClassLoader parent, @Nullable ClassLoader additionalClassloader, boolean delegateResourceLoading)71 AdapterClassLoader(@NonNull String dexPath, @Nullable String librarySearchPath, 72 @Nullable ClassLoader parent, @Nullable ClassLoader additionalClassloader, 73 boolean delegateResourceLoading) { 74 super(dexPath, librarySearchPath, parent); 75 mSharedLibraryClassLoader = additionalClassloader; 76 } 77 78 /** 79 * A copy from {@link dalvik.system.DelegateLastClassLoader}, but uses both {@code parent} 80 * and {@code additionalClassLoader} from the constructor as parent classloaders. 81 * 82 * If AdapterClassLoader are loaded, loading them 83 * from this classloader will be skipped and instead they'll be loaded from the parent 84 * classloader, so that they are not duplicated. 85 */ 86 @Override loadClass(String name, boolean resolve)87 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 88 // First, check whether the class has already been loaded. Return it if that's the case. 89 Class<?> cl = findLoadedClass(name); 90 if (cl != null) { 91 return cl; 92 } 93 94 // Next, check whether the class in question is present in the boot classpath. 95 try { 96 return Object.class.getClassLoader().loadClass(name); 97 } catch (ClassNotFoundException ignored) { 98 // It's fine, just continue 99 } 100 101 ClassNotFoundException fromSuper = null; 102 103 // Only load adapter classes and certain util classes in this classloader. 104 if (name != null && PATTERN.matcher(name).find()) { 105 // Next, check whether the class in question is present in the dexPath that this 106 // classloader operates on, or its shared libraries. 107 try { 108 return findClass(name); 109 } catch (ClassNotFoundException ex) { 110 fromSuper = ex; 111 } 112 } 113 114 // Loading OEM-APIs 115 // Next, check any additional classloaders. 116 if (mSharedLibraryClassLoader != null && SHARED_LIBRARY_PATTERN.matcher(name).matches()) { 117 try { 118 return mSharedLibraryClassLoader.loadClass(name); 119 } catch (ClassNotFoundException ignored) { 120 // It's fine, just continue 121 } 122 } 123 124 // Finally, check whether the class in question is present in the parent classloader. 125 try { 126 return getParent().loadClass(name); 127 } catch (ClassNotFoundException cnfe) { 128 // The exception we're catching here is the CNFE thrown by the parent of this 129 // classloader. However, we would like to throw a CNFE that provides details about 130 // the class path / list of dex files associated with *this* classloader, so we choose 131 // to throw the exception thrown from that lookup. 132 if (fromSuper == null) { 133 throw cnfe; 134 } 135 throw fromSuper; 136 } 137 } 138 } 139