1 /*
2  * Copyright (C) 2016 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 
17 package com.android.internal.os;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.os.Build;
21 import android.os.Trace;
22 
23 import dalvik.system.DelegateLastClassLoader;
24 import dalvik.system.DexClassLoader;
25 import dalvik.system.PathClassLoader;
26 
27 import java.util.ArrayList;
28 import java.util.List;
29 
30 /**
31  * Creates class loaders.
32  *
33  * @hide
34  */
35 public class ClassLoaderFactory {
36     // Unconstructable
ClassLoaderFactory()37     private ClassLoaderFactory() {}
38 
39     private static final String PATH_CLASS_LOADER_NAME = PathClassLoader.class.getName();
40     private static final String DEX_CLASS_LOADER_NAME = DexClassLoader.class.getName();
41     private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
42             DelegateLastClassLoader.class.getName();
43 
44     /**
45      * Returns the name of the class for PathClassLoader.
46      */
getPathClassLoaderName()47     public static String getPathClassLoaderName() {
48         return PATH_CLASS_LOADER_NAME;
49     }
50 
51     /**
52      * Returns true if {@code name} is a supported classloader. {@code name} must be a
53      * binary name of a class, as defined by {@code Class.getName}.
54      */
isValidClassLoaderName(String name)55     public static boolean isValidClassLoaderName(String name) {
56         // This method is used to parse package data and does not accept null names.
57         return name != null && (isPathClassLoaderName(name) || isDelegateLastClassLoaderName(name));
58     }
59 
60     /**
61      * Returns true if {@code name} is the encoding for either PathClassLoader or DexClassLoader.
62      * The two class loaders are grouped together because they have the same behaviour.
63      */
isPathClassLoaderName(String name)64     public static boolean isPathClassLoaderName(String name) {
65         // For null values we default to PathClassLoader. This cover the case when packages
66         // don't specify any value for their class loaders.
67         return name == null || PATH_CLASS_LOADER_NAME.equals(name) ||
68                 DEX_CLASS_LOADER_NAME.equals(name);
69     }
70 
71     /**
72      * Returns true if {@code name} is the encoding for the DelegateLastClassLoader.
73      */
isDelegateLastClassLoaderName(String name)74     public static boolean isDelegateLastClassLoaderName(String name) {
75         return DELEGATE_LAST_CLASS_LOADER_NAME.equals(name);
76     }
77 
78     /**
79      * Same as {@code createClassLoader} below, except that no associated namespace
80      * is created.
81      */
createClassLoader(String dexPath, String librarySearchPath, ClassLoader parent, String classloaderName, List<ClassLoader> sharedLibraries, List<ClassLoader> sharedLibrariesLoadedAfter)82     public static ClassLoader createClassLoader(String dexPath,
83             String librarySearchPath, ClassLoader parent, String classloaderName,
84             List<ClassLoader> sharedLibraries, List<ClassLoader> sharedLibrariesLoadedAfter) {
85         ClassLoader[] arrayOfSharedLibraries = (sharedLibraries == null)
86                 ? null
87                 : sharedLibraries.toArray(new ClassLoader[sharedLibraries.size()]);
88         ClassLoader[] arrayOfSharedLibrariesLoadedAfterApp = (sharedLibrariesLoadedAfter == null)
89                 ? null
90                 : sharedLibrariesLoadedAfter.toArray(
91                         new ClassLoader[sharedLibrariesLoadedAfter.size()]);
92         if (isPathClassLoaderName(classloaderName)) {
93             return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries,
94                     arrayOfSharedLibrariesLoadedAfterApp);
95         } else if (isDelegateLastClassLoaderName(classloaderName)) {
96             return new DelegateLastClassLoader(dexPath, librarySearchPath, parent,
97                     arrayOfSharedLibraries, arrayOfSharedLibrariesLoadedAfterApp);
98         }
99 
100         throw new AssertionError("Invalid classLoaderName: " + classloaderName);
101     }
102 
103     /**
104      * Same as {@code createClassLoader} below, but passes a null list of shared libraries. This
105      * method is used only to load platform classes (i.e. those in framework.jar or services.jar),
106      * and MUST NOT be used for loading untrusted classes, especially the app classes. For the
107      * latter case, use the below method which accepts list of shared libraries so that the classes
108      * don't have unlimited access to all shared libraries.
109      */
createClassLoader(String dexPath, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, int targetSdkVersion, boolean isNamespaceShared, String classLoaderName)110     public static ClassLoader createClassLoader(String dexPath,
111             String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
112             int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) {
113         // b/205164833: allow framework classes to have access to all public vendor libraries.
114         // This is because those classes are part of the platform and don't have an app manifest
115         // where required libraries can be specified using the <uses-native-library> tag.
116         // Note that this still does not give access to "private" vendor libraries.
117         List<String> nativeSharedLibraries = new ArrayList<>();
118         nativeSharedLibraries.add("ALL");
119 
120         return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath,
121             parent, targetSdkVersion, isNamespaceShared, classLoaderName, null,
122             nativeSharedLibraries, null);
123     }
124 
125     /**
126      * Create a ClassLoader and initialize a linker-namespace for it.
127      */
createClassLoader(String dexPath, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, int targetSdkVersion, boolean isNamespaceShared, String classLoaderName, List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries, List<ClassLoader> sharedLibrariesAfter)128     public static ClassLoader createClassLoader(String dexPath,
129             String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
130             int targetSdkVersion, boolean isNamespaceShared, String classLoaderName,
131             List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries,
132             List<ClassLoader> sharedLibrariesAfter) {
133 
134         final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
135                 classLoaderName, sharedLibraries, sharedLibrariesAfter);
136 
137         String sonameList = "";
138         if (nativeSharedLibraries != null) {
139             sonameList = String.join(":", nativeSharedLibraries);
140         }
141 
142         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
143         String errorMessage = createClassloaderNamespace(classLoader,
144                                                          targetSdkVersion,
145                                                          librarySearchPath,
146                                                          libraryPermittedPath,
147                                                          isNamespaceShared,
148                                                          dexPath,
149                                                          sonameList);
150         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
151 
152         if (errorMessage != null) {
153             throw new UnsatisfiedLinkError("Unable to create namespace for the classloader " +
154                                            classLoader + ": " + errorMessage);
155         }
156 
157         return classLoader;
158     }
159 
160     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
createClassloaderNamespace(ClassLoader classLoader, int targetSdkVersion, String librarySearchPath, String libraryPermittedPath, boolean isNamespaceShared, String dexPath, String sonameList)161     private static native String createClassloaderNamespace(ClassLoader classLoader,
162                                                             int targetSdkVersion,
163                                                             String librarySearchPath,
164                                                             String libraryPermittedPath,
165                                                             boolean isNamespaceShared,
166                                                             String dexPath,
167                                                             String sonameList);
168 }
169