1/*
2 * Copyright (C) 2006 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
17package android.app;
18
19import android.os.Build;
20import android.os.Trace;
21import android.util.ArrayMap;
22import com.android.internal.os.ClassLoaderFactory;
23import dalvik.system.PathClassLoader;
24
25/** @hide */
26public class ApplicationLoaders {
27    public static ApplicationLoaders getDefault() {
28        return gApplicationLoaders;
29    }
30
31    ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
32                               String librarySearchPath, String libraryPermittedPath,
33                               ClassLoader parent, String classLoaderName) {
34        // For normal usage the cache key used is the same as the zip path.
35        return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
36                              libraryPermittedPath, parent, zip, classLoaderName);
37    }
38
39    private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
40                                       String librarySearchPath, String libraryPermittedPath,
41                                       ClassLoader parent, String cacheKey,
42                                       String classLoaderName) {
43        /*
44         * This is the parent we use if they pass "null" in.  In theory
45         * this should be the "system" class loader; in practice we
46         * don't use that and can happily (and more efficiently) use the
47         * bootstrap class loader.
48         */
49        ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();
50
51        synchronized (mLoaders) {
52            if (parent == null) {
53                parent = baseParent;
54            }
55
56            /*
57             * If we're one step up from the base class loader, find
58             * something in our cache.  Otherwise, we create a whole
59             * new ClassLoader for the zip archive.
60             */
61            if (parent == baseParent) {
62                ClassLoader loader = mLoaders.get(cacheKey);
63                if (loader != null) {
64                    return loader;
65                }
66
67                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
68
69                ClassLoader classloader = ClassLoaderFactory.createClassLoader(
70                        zip,  librarySearchPath, libraryPermittedPath, parent,
71                        targetSdkVersion, isBundled, classLoaderName);
72
73                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
74
75                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setupVulkanLayerPath");
76                setupVulkanLayerPath(classloader, librarySearchPath);
77                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
78
79                mLoaders.put(cacheKey, classloader);
80                return classloader;
81            }
82
83            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
84            ClassLoader loader = ClassLoaderFactory.createClassLoader(
85                    zip, null, parent, classLoaderName);
86            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
87            return loader;
88        }
89    }
90
91    /**
92     * Creates a classloader for the WebView APK and places it in the cache of loaders maintained
93     * by this class. This is used in the WebView zygote, where its presence in the cache speeds up
94     * startup and enables memory sharing.
95     */
96    public ClassLoader createAndCacheWebViewClassLoader(String packagePath, String libsPath,
97                                                        String cacheKey) {
98        // The correct paths are calculated by WebViewZygote in the system server and passed to
99        // us here. We hardcode the other parameters: WebView always targets the current SDK,
100        // does not need to use non-public system libraries, and uses the base classloader as its
101        // parent to permit usage of the cache.
102        // The cache key is passed separately to enable the stub WebView to be cached under the
103        // stub's APK path, when the actual package path is the donor APK.
104        return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
105                              cacheKey, null /* classLoaderName */);
106    }
107
108    private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath);
109
110    /**
111     * Adds a new path the classpath of the given loader.
112     * @throws IllegalStateException if the provided class loader is not a {@link PathClassLoader}.
113     */
114    void addPath(ClassLoader classLoader, String dexPath) {
115        if (!(classLoader instanceof PathClassLoader)) {
116            throw new IllegalStateException("class loader is not a PathClassLoader");
117        }
118        final PathClassLoader baseDexClassLoader = (PathClassLoader) classLoader;
119        baseDexClassLoader.addDexPath(dexPath);
120    }
121
122    private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<>();
123
124    private static final ApplicationLoaders gApplicationLoaders = new ApplicationLoaders();
125}
126