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.text.TextUtils;
22import android.util.ArrayMap;
23import com.android.internal.os.PathClassLoaderFactory;
24import dalvik.system.PathClassLoader;
25
26/** @hide */
27public class ApplicationLoaders {
28    public static ApplicationLoaders getDefault() {
29        return gApplicationLoaders;
30    }
31
32    ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
33                               String librarySearchPath, String libraryPermittedPath,
34                               ClassLoader parent) {
35        // For normal usage the cache key used is the same as the zip path.
36        return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
37                              libraryPermittedPath, parent, zip);
38    }
39
40    private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
41                                       String librarySearchPath, String libraryPermittedPath,
42                                       ClassLoader parent, String cacheKey) {
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                PathClassLoader pathClassloader = PathClassLoaderFactory.createClassLoader(
70                                                      zip,
71                                                      librarySearchPath,
72                                                      libraryPermittedPath,
73                                                      parent,
74                                                      targetSdkVersion,
75                                                      isBundled);
76
77                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
78
79                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setupVulkanLayerPath");
80                setupVulkanLayerPath(pathClassloader, librarySearchPath);
81                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
82
83                mLoaders.put(cacheKey, pathClassloader);
84                return pathClassloader;
85            }
86
87            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
88            PathClassLoader pathClassloader = new PathClassLoader(zip, parent);
89            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
90            return pathClassloader;
91        }
92    }
93
94    /**
95     * Creates a classloader for the WebView APK and places it in the cache of loaders maintained
96     * by this class. This is used in the WebView zygote, where its presence in the cache speeds up
97     * startup and enables memory sharing.
98     */
99    public ClassLoader createAndCacheWebViewClassLoader(String packagePath, String libsPath,
100                                                        String cacheKey) {
101        // The correct paths are calculated by WebViewZygote in the system server and passed to
102        // us here. We hardcode the other parameters: WebView always targets the current SDK,
103        // does not need to use non-public system libraries, and uses the base classloader as its
104        // parent to permit usage of the cache.
105        // The cache key is passed separately to enable the stub WebView to be cached under the
106        // stub's APK path, when the actual package path is the donor APK.
107        return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
108                              cacheKey);
109    }
110
111    private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath);
112
113    /**
114     * Adds a new path the classpath of the given loader.
115     * @throws IllegalStateException if the provided class loader is not a {@link PathClassLoader}.
116     */
117    void addPath(ClassLoader classLoader, String dexPath) {
118        if (!(classLoader instanceof PathClassLoader)) {
119            throw new IllegalStateException("class loader is not a PathClassLoader");
120        }
121        final PathClassLoader baseDexClassLoader = (PathClassLoader) classLoader;
122        baseDexClassLoader.addDexPath(dexPath);
123    }
124
125    private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<String, ClassLoader>();
126
127    private static final ApplicationLoaders gApplicationLoaders = new ApplicationLoaders();
128}
129