| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.car.ui.pluginsupport; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| |
| import dalvik.system.PathClassLoader; |
| |
| import java.util.regex.Pattern; |
| |
| /** |
| * This is a {@link PathClassLoader} that you can pass an additional classloaders to the |
| * constructor. That will be searched after the classes in this classloader have been searched, |
| * but before the parent classloader is searched. The first classloader in the list of |
| * classloaders is set as the parent classloader. |
| * |
| * Much of the code is copied from {@link dalvik.system.DelegateLastClassLoader}. However, |
| * note that resource loading is not implemented the same way just because we didn't need it. |
| */ |
| class AdapterClassLoader extends PathClassLoader { |
| |
| @Nullable |
| private final ClassLoader mPluginClassLoader; |
| |
| private static final Pattern PATTERN = Pattern.compile( |
| "^com\\.android\\.car\\.ui\\..*AdapterV[0-9]+(\\$.*)?$" |
| + "|Lambda" |
| + "|^" + Pattern.quote(OemApiUtil.class.getName()) + "$"); |
| |
| private static final Pattern PLUGIN_PATTERN = Pattern.compile( |
| "^com\\.android\\.car\\.ui\\.plugin\\.(oemapis\\..*|PluginVersionProviderImpl)$"); |
| |
| /** |
| * Equivalent to calling {@link #AdapterClassLoader(String, String, ClassLoader, ClassLoader)} |
| * with {@code librarySearchPath = null, delegateResourceLoading = true}. |
| */ |
| AdapterClassLoader(String dexPath, @Nullable ClassLoader parent, |
| @Nullable ClassLoader additionalClassloader) { |
| this(dexPath, null, parent, additionalClassloader); |
| } |
| |
| /** |
| * Equivalent to calling |
| * {@link #AdapterClassLoader(String, String, ClassLoader, ClassLoader, boolean)} |
| * with {@code delegateResourceLoading = true}. |
| */ |
| AdapterClassLoader(String dexPath, String librarySearchPath, @Nullable ClassLoader parent, |
| @Nullable ClassLoader additionalClassloader) { |
| this(dexPath, librarySearchPath, parent, additionalClassloader, true); |
| } |
| |
| /** |
| * See {@link dalvik.system.DelegateLastClassLoader#DelegateLastClassLoader |
| * (String, String, List<ClassLoader>, boolean)}. |
| */ |
| AdapterClassLoader(@NonNull String dexPath, @Nullable String librarySearchPath, |
| @Nullable ClassLoader parent, @Nullable ClassLoader additionalClassloader, |
| boolean delegateResourceLoading) { |
| super(dexPath, librarySearchPath, parent); |
| mPluginClassLoader = additionalClassloader; |
| } |
| |
| /** |
| * A copy from {@link dalvik.system.DelegateLastClassLoader}, but uses both {@code parent} |
| * and {@code additionalClassLoader} from the constructor as parent classloaders. |
| * |
| * If AdapterClassLoader are loaded, loading them |
| * from this classloader will be skipped and instead they'll be loaded from the parent |
| * classloader, so that they are not duplicated. |
| */ |
| @Override |
| protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { |
| // First, check whether the class has already been loaded. Return it if that's the case. |
| Class<?> cl = findLoadedClass(name); |
| if (cl != null) { |
| return cl; |
| } |
| |
| // Next, check whether the class in question is present in the boot classpath. |
| try { |
| return Object.class.getClassLoader().loadClass(name); |
| } catch (ClassNotFoundException ignored) { |
| // It's fine, just continue |
| } |
| |
| ClassNotFoundException fromSuper = null; |
| |
| // Only load adapter classes and certain util classes in this classloader. |
| if (name != null && PATTERN.matcher(name).find()) { |
| try { |
| return findClass(name); |
| } catch (ClassNotFoundException ex) { |
| fromSuper = ex; |
| } |
| } |
| |
| // Load only OEM-APIs from the plugin classloader |
| if (mPluginClassLoader != null && PLUGIN_PATTERN.matcher(name).matches()) { |
| try { |
| return mPluginClassLoader.loadClass(name); |
| } catch (ClassNotFoundException ignored) { |
| // It's fine, just continue |
| } |
| } |
| |
| // Finally, check whether the class in question is present in the parent classloader. |
| try { |
| return getParent().loadClass(name); |
| } catch (ClassNotFoundException cnfe) { |
| // The exception we're catching here is the CNFE thrown by the parent of this |
| // classloader. However, we would like to throw a CNFE that provides details about |
| // the class path / list of dex files associated with *this* classloader, so we choose |
| // to throw the exception thrown from that lookup. |
| if (fromSuper == null) { |
| throw cnfe; |
| } |
| throw fromSuper; |
| } |
| } |
| } |