/* * Copyright (C) 2011 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 dalvik.system; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import libcore.util.NonNull; import libcore.util.Nullable; import sun.misc.CompoundEnumeration; /** * Base class for common functionality between various dex-based * {@link ClassLoader} implementations. */ public class BaseDexClassLoader extends ClassLoader { /** * Hook for customizing how dex files loads are reported. * * This enables the framework to monitor the use of dex files. The * goal is to simplify the mechanism for optimizing foreign dex files and * enable further optimizations of secondary dex files. * * The reporting happens only when new instances of BaseDexClassLoader * are constructed and will be active only after this field is set with * {@link BaseDexClassLoader#setReporter}. */ /* @NonNull */ private static volatile Reporter reporter = null; @UnsupportedAppUsage private final DexPathList pathList; /** * Array of ClassLoaders that can be used to load classes and resources that the code in * {@code pathList} may depend on. This is used to implement Android's * * shared libraries feature. *
The shared library loaders are always checked before the {@code pathList} when looking * up classes and resources. * *
{@code null} if the class loader has no shared library. * * @hide */ protected final ClassLoader[] sharedLibraryLoaders; /** * Array of ClassLoaders identical to {@code sharedLibraryLoaders} except that these library * loaders are always checked after the {@code pathList} when looking up classes and resources. * * The placement of a library into this group is done by the OEM and cannot be configured by * an App. * *
{@code null} if the class loader has no shared library. * * @hide */ protected final ClassLoader[] sharedLibraryLoadersAfter; /** * Constructs an instance. * Note that all the *.jar and *.apk files from {@code dexPath} might be * first extracted in-memory before the code is loaded. This can be avoided * by passing raw dex files (*.dex) in the {@code dexPath}. * * @param dexPath the list of jar/apk files containing classes and * resources, delimited by {@code File.pathSeparator}, which * defaults to {@code ":"} on Android. * @param optimizedDirectory this parameter is deprecated and has no effect since API level 26. * @param librarySearchPath the list of directories containing native * libraries, delimited by {@code File.pathSeparator}; may be * {@code null} * @param parent the parent class loader */ public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) { this(dexPath, librarySearchPath, parent, null, null, false); } /** * @hide */ @UnsupportedAppUsage public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent, boolean isTrusted) { this(dexPath, librarySearchPath, parent, null, null, isTrusted); } /** * @hide */ public BaseDexClassLoader(String dexPath, String librarySearchPath, ClassLoader parent, ClassLoader[] libraries) { this(dexPath, librarySearchPath, parent, libraries, null, false); } /** * @hide */ public BaseDexClassLoader(String dexPath, String librarySearchPath, ClassLoader parent, ClassLoader[] libraries, ClassLoader[] librariesAfter) { this(dexPath, librarySearchPath, parent, libraries, librariesAfter, false); } /** * BaseDexClassLoader implements the Android * * shared libraries feature by changing the typical parent delegation mechanism * of class loaders. *
Each shared library is associated with its own class loader, which is added to a list of * class loaders this BaseDexClassLoader tries to load from in order, immediately checking * after the parent. * The shared library loaders are always checked before the {@code pathList} when looking * up classes and resources. *
* The shared library loaders defined in sharedLibraryLoadersAfter are always checked
* after the {@code pathList}
*
* @hide
*/
public BaseDexClassLoader(String dexPath,
String librarySearchPath, ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
ClassLoader[] sharedLibraryLoadersAfter,
boolean isTrusted) {
super(parent);
// Setup shared libraries before creating the path list. ART relies on the class loader
// hierarchy being finalized before loading dex files.
this.sharedLibraryLoaders = sharedLibraryLoaders == null
? null
: Arrays.copyOf(sharedLibraryLoaders, sharedLibraryLoaders.length);
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
this.sharedLibraryLoadersAfter = sharedLibraryLoadersAfter == null
? null
: Arrays.copyOf(sharedLibraryLoadersAfter, sharedLibraryLoadersAfter.length);
// Run background verification after having set 'pathList'.
this.pathList.maybeRunBackgroundVerification(this);
reportClassLoaderChain();
}
/**
* Reports the current class loader chain to the registered {@code reporter}.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public void reportClassLoaderChain() {
if (reporter == null) {
return;
}
String[] classPathAndClassLoaderContexts = computeClassLoaderContextsNative();
if (classPathAndClassLoaderContexts.length == 0) {
return;
}
Map There is a limited chance that we end up with multiple
* {@code Package} objects representing the same package: It can
* happen when when a package is scattered across different JAR
* files which were loaded by different {@code ClassLoader}
* instances. This is rather unlikely, and given that this whole
* thing is more or less a workaround, probably not worth the
* effort to address.
*
* @param name the name of the class
* @return the package information for the class, or {@code null}
* if there is no package information available for it
*
* @deprecated See {@link ClassLoader#getPackage(String)}
*/
@Deprecated
@Override
protected synchronized Package getPackage(String name) {
if (name != null && !name.isEmpty()) {
Package pack = super.getPackage(name);
if (pack == null) {
pack = definePackage(name, "Unknown", "0.0", "Unknown",
"Unknown", "0.0", "Unknown", null);
}
return pack;
}
return null;
}
/**
* Returns colon-separated set of directories where libraries should be
* searched for first, before the standard set of directories.
*
* @return colon-separated set of search directories
*
* @hide
*/
@UnsupportedAppUsage
@SystemApi(client = MODULE_LIBRARIES)
public @NonNull String getLdLibraryPath() {
StringBuilder result = new StringBuilder();
for (File directory : pathList.getNativeLibraryDirectories()) {
if (result.length() > 0) {
result.append(':');
}
result.append(directory);
}
return result.toString();
}
@Override public String toString() {
return getClass().getName() + "[" + pathList + "]";
}
/**
* Sets the reporter for dex load notifications.
* Once set, all new instances of BaseDexClassLoader will report upon
* constructions the loaded dex files.
*
* @param newReporter the new Reporter. Setting {@code null} will cancel reporting.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static void setReporter(@Nullable Reporter newReporter) {
reporter = newReporter;
}
/**
* @hide
*/
public static Reporter getReporter() {
return reporter;
}
/**
* Reports the construction of a {@link BaseDexClassLoader} and provides opaque
* information about the class loader chain.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public interface Reporter {
/**
* Reports the construction of a BaseDexClassLoader and provides opaque information about
* the class loader chain. For example, if the childmost ClassLoader in the chain:
* {@quote BaseDexClassLoader { foo.dex } -> BaseDexClassLoader { base.apk }
* -> BootClassLoader } was just initialized then the load of {@code "foo.dex"} would be
* reported with a classLoaderContext of {@code "PCL[];PCL[base.apk]"}.
*
* @param contextsMap A map from dex file paths to the class loader context used to load
* each dex file.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
void report(@NonNull Map