1 /* 2 * Copyright (C) 2021 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.os.Build; 20 import android.util.ArrayMap; 21 22 import dalvik.system.PathClassLoader; 23 24 /** @hide */ 25 public final class SystemServerClassLoaderFactory { 26 /** 27 * Map of paths to PathClassLoader for standalone system server jars. 28 */ 29 private static final ArrayMap<String, PathClassLoader> sLoadedPaths = new ArrayMap<>(); 30 31 /** 32 * Creates and caches a ClassLoader for the jar at the given path. 33 * 34 * This method should only be called by ZygoteInit to prefetch jars. For other users, use 35 * {@link getOrCreateClassLoader} instead. 36 * 37 * The parent class loader should always be the system server class loader. Changing it has 38 * implications that require discussion with the mainline team. 39 * 40 * @hide for internal use only 41 */ createClassLoader(String path, ClassLoader parent)42 /* package */ static PathClassLoader createClassLoader(String path, ClassLoader parent) { 43 if (sLoadedPaths.containsKey(path)) { 44 throw new IllegalStateException("A ClassLoader for " + path + " already exists"); 45 } 46 PathClassLoader pathClassLoader = (PathClassLoader) ClassLoaderFactory.createClassLoader( 47 path, /*librarySearchPath=*/null, /*libraryPermittedPath=*/null, parent, 48 Build.VERSION.SDK_INT, /*isNamespaceShared=*/true , /*classLoaderName=*/null); 49 sLoadedPaths.put(path, pathClassLoader); 50 return pathClassLoader; 51 } 52 53 /** 54 * Returns a cached ClassLoader to be used at runtime for the jar at the given path. Or, creates 55 * one if it is not prefetched and is allowed to be created at runtime. 56 * 57 * The parent class loader should always be the system server class loader. Changing it has 58 * implications that require discussion with the mainline team. 59 * 60 * @hide for internal use only 61 */ getOrCreateClassLoader( String path, ClassLoader parent, boolean isTestOnly)62 public static PathClassLoader getOrCreateClassLoader( 63 String path, ClassLoader parent, boolean isTestOnly) { 64 PathClassLoader pathClassLoader = sLoadedPaths.get(path); 65 if (pathClassLoader != null) { 66 return pathClassLoader; 67 } 68 if (!allowClassLoaderCreation(path, isTestOnly)) { 69 throw new RuntimeException("Creating a ClassLoader from " + path + " is not allowed. " 70 + "Please make sure that the jar is listed in " 71 + "`PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS` in the Makefile and added as a " 72 + "`standalone_contents` of a `systemserverclasspath_fragment` in " 73 + "`Android.bp`."); 74 } 75 return createClassLoader(path, parent); 76 } 77 78 /** 79 * Returns whether a class loader for the jar is allowed to be created at runtime. 80 */ allowClassLoaderCreation(String path, boolean isTestOnly)81 private static boolean allowClassLoaderCreation(String path, boolean isTestOnly) { 82 // Currently, we only enforce prefetching for APEX jars. 83 if (!path.startsWith("/apex/")) { 84 return true; 85 } 86 // APEXes for testing only are okay to ignore. 87 if (isTestOnly) { 88 return true; 89 } 90 // If system server is being profiled, it's OK to create class loaders anytime. 91 if (ZygoteInit.shouldProfileSystemServer()) { 92 return true; 93 } 94 return false; 95 } 96 97 98 } 99