diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index a9e8fbce91872..0e02af2eab289 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -127,6 +127,12 @@ public class ZygoteInit { private static boolean sPreloadComplete; + /** + * Cached classloader to use for the system server. Will only be populated in the system + * server process. + */ + private static ClassLoader sCachedSystemServerClassLoader = null; + static void preload(TimingsTraceLog bootTimingsTraceLog) { Log.d(TAG, "begin preload"); bootTimingsTraceLog.traceBegin("BeginPreload"); @@ -543,10 +549,8 @@ public class ZygoteInit { throw new IllegalStateException("Unexpected return from WrapperInit.execApplication"); } else { - ClassLoader cl = null; - if (systemServerClasspath != null) { - cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion); - + ClassLoader cl = getOrCreateSystemServerClassLoader(); + if (cl != null) { Thread.currentThread().setContextClassLoader(cl); } @@ -561,6 +565,23 @@ public class ZygoteInit { /* should never reach here */ } + /** + * Create the classloader for the system server and store it in + * {@link sCachedSystemServerClassLoader}. This function may be called through JNI in + * system server startup, when the runtime is in a critically low state. Do not do + * extended computation etc here. + */ + private static ClassLoader getOrCreateSystemServerClassLoader() { + if (sCachedSystemServerClassLoader == null) { + final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH"); + if (systemServerClasspath != null) { + sCachedSystemServerClassLoader = createPathClassLoader(systemServerClasspath, + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT); + } + } + return sCachedSystemServerClassLoader; + } + /** * Note that preparing the profiles for system server does not require special selinux * permissions. From the installer perspective the system server is a regular package which can diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index fa9eead6b7001..55a398e7a0849 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -122,6 +122,10 @@ static jclass gZygoteClass; static jmethodID gCallPostForkSystemServerHooks; static jmethodID gCallPostForkChildHooks; +static constexpr const char* kZygoteInitClassName = "com/android/internal/os/ZygoteInit"; +static jclass gZygoteInitClass; +static jmethodID gGetOrCreateSystemServerClassLoader; + static bool gIsSecurityEnforced = true; /** @@ -1572,6 +1576,17 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, instruction_set.value().c_str()); } + if (is_system_server) { + // Prefetch the classloader for the system server. This is done early to + // allow a tie-down of the proper system server selinux domain. + env->CallStaticVoidMethod(gZygoteInitClass, gGetOrCreateSystemServerClassLoader); + if (env->ExceptionCheck()) { + // Be robust here. The Java code will attempt to create the classloader + // at a later point (but may not have rights to use AoT artifacts). + env->ExceptionClear(); + } + } + if (setresgid(gid, gid, gid) == -1) { fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno))); } @@ -2635,6 +2650,13 @@ int register_com_android_internal_os_Zygote(JNIEnv* env) { gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks", "(IZZLjava/lang/String;)V"); - return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods)); + gZygoteInitClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteInitClassName)); + gGetOrCreateSystemServerClassLoader = + GetStaticMethodIDOrDie(env, gZygoteInitClass, "getOrCreateSystemServerClassLoader", + "()Ljava/lang/ClassLoader;"); + + RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods)); + + return JNI_OK; } } // namespace android