• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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