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