Replace a custom AndroidRuntime::findClass with a more targeted fix.

This seems simpler and more contained, and I think the comment explaining
why hoop-jumping is necessary is a bit clearer now.

Change-Id: Ief4afd7cbb42188ed835fce23e497520bdb753a8
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 0159edd..371268f 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -1,8 +1,8 @@
 /*
  * Main entry of app process.
- * 
+ *
  * Starts the interpreted runtime, then starts up the application.
- * 
+ *
  */
 
 #define LOG_TAG "appproc"
@@ -25,23 +25,13 @@
         "Usage: app_process [java-options] cmd-dir start-class-name [options]\n");
 }
 
-status_t app_init(const char* className, int argc, const char* const argv[])
-{
-    LOGV("Entered app_init()!\n");
-
-    AndroidRuntime* jr = AndroidRuntime::getRuntime();
-    jr->callMain(className, argc, argv);
-    
-    LOGV("Exiting app_init()!\n");
-    return NO_ERROR;
-}
-
 class AppRuntime : public AndroidRuntime
 {
 public:
     AppRuntime()
         : mParentDir(NULL)
         , mClassName(NULL)
+        , mClass(NULL)
         , mArgC(0)
         , mArgV(NULL)
     {
@@ -60,6 +50,35 @@
         return mClassName;
     }
 
+    virtual void onVmCreated(JNIEnv* env)
+    {
+        if (mClassName == NULL) {
+            return; // Zygote. Nothing to do here.
+        }
+
+        /*
+         * This is a little awkward because the JNI FindClass call uses the
+         * class loader associated with the native method we're executing in.
+         * If called in onStarted (from RuntimeInit.finishInit because we're
+         * launching "am", for example), FindClass would see that we're calling
+         * from a boot class' native method, and so wouldn't look for the class
+         * we're trying to look up in CLASSPATH. Unfortunately it needs to,
+         * because the "am" classes are not boot classes.
+         *
+         * The easiest fix is to call FindClass here, early on before we start
+         * executing boot class Java code and thereby deny ourselves access to
+         * non-boot classes.
+         */
+        char* slashClassName = toSlashClassName(mClassName);
+        mClass = env->FindClass(slashClassName);
+        if (mClass == NULL) {
+            LOGE("ERROR: could not find class '%s'\n", mClassName);
+        }
+        free(slashClassName);
+
+        mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
+    }
+
     virtual void onStarted()
     {
         sp<ProcessState> proc = ProcessState::self();
@@ -67,8 +86,9 @@
             LOGV("App process: starting thread pool.\n");
             proc->startThreadPool();
         }
-        
-        app_init(mClassName, mArgC, mArgV);
+
+        AndroidRuntime* ar = AndroidRuntime::getRuntime();
+        ar->callMain(mClassName, mClass, mArgC, mArgV);
 
         if (ProcessState::self()->supportsProcesses()) {
             IPCThreadState::self()->stopProcess();
@@ -81,7 +101,7 @@
         if (proc->supportsProcesses()) {
             LOGV("App process: starting thread pool.\n");
             proc->startThreadPool();
-        }       
+        }
     }
 
     virtual void onExit(int code)
@@ -96,9 +116,10 @@
         AndroidRuntime::onExit(code);
     }
 
-    
+
     const char* mParentDir;
     const char* mClassName;
+    jclass mClass;
     int mArgC;
     const char* const* mArgV;
 };
@@ -120,7 +141,7 @@
     // These are global variables in ProcessState.cpp
     mArgC = argc;
     mArgV = argv;
-    
+
     mArgLen = 0;
     for (int i=0; i<argc; i++) {
         mArgLen += strlen(argv[i]) + 1;
@@ -139,7 +160,7 @@
     argv++;
 
     // Everything up to '--' or first non '-' arg goes to the vm
-    
+
     int i = runtime.addVmArguments(argc, argv);
 
     // Next arg is parent directory
@@ -151,7 +172,7 @@
     if (i < argc) {
         arg = argv[i++];
         if (0 == strcmp("--zygote", arg)) {
-            bool startSystemServer = (i < argc) ? 
+            bool startSystemServer = (i < argc) ?
                     strcmp(argv[i], "--start-system-server") == 0 : false;
             setArgv0(argv0, "zygote");
             set_process_name("zygote");
diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp
index 83cb533..785e4cc 100644
--- a/cmds/runtime/main_runtime.cpp
+++ b/cmds/runtime/main_runtime.cpp
@@ -12,7 +12,7 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
-#include <utils/Log.h>  
+#include <utils/Log.h>
 #include <cutils/zygote.h>
 
 #include <cutils/properties.h>
@@ -41,7 +41,7 @@
 #undef LOG_TAG
 #define LOG_TAG "runtime"
 
-static const char* ZYGOTE_ARGV[] = { 
+static const char* ZYGOTE_ARGV[] = {
     "--setuid=1000",
     "--setgid=1000",
     "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
@@ -68,7 +68,6 @@
 
 namespace android {
 
-extern status_t app_init(const char* className);
 extern void set_finish_init_func(void (*func)());
 
 
@@ -76,7 +75,7 @@
  * This class is used to kill this process (runtime) when the system_server dies.
  */
 class GrimReaper : public IBinder::DeathRecipient {
-public: 
+public:
     GrimReaper() { }
 
     virtual void binderDied(const wp<IBinder>& who)
@@ -170,7 +169,7 @@
 
 /*
  * Post-system-process initialization.
- * 
+ *
  * This function continues initialization after the system process
  * has been initialized.  It needs to be separate because the system
  * initialization needs to care of starting the Android runtime if it is not
@@ -210,17 +209,17 @@
 static void boot_init()
 {
     LOGI("Entered boot_init()!\n");
-    
+
     sp<ProcessState> proc(ProcessState::self());
     LOGD("ProcessState: %p\n", proc.get());
     proc->becomeContextManager(contextChecker, NULL);
-    
+
     if (proc->supportsProcesses()) {
         LOGI("Binder driver opened.  Multiprocess enabled.\n");
     } else {
         LOGI("Binder driver not found.  Processes not supported.\n");
     }
-    
+
     sp<BServiceManager> sm = new BServiceManager;
     proc->setContextObject(sm);
 }
@@ -258,7 +257,7 @@
     int res;
     time_t min_time = 1167652800; // jan 1 2007, type 'date -ud "1/1 12:00" +%s' to get value for current year
     struct timespec ts;
-    
+
     fd = open("/dev/alarm", O_RDWR);
     if(fd < 0) {
         LOGW("Unable to open alarm driver: %s\n", strerror(errno));
@@ -346,14 +345,14 @@
     int ic;
     int result = 1;
     pid_t systemPid;
-    
+
     sp<ProcessState> proc;
 
 #ifndef HAVE_ANDROID_OS
     /* Set stdout/stderr to unbuffered for MinGW/MSYS. */
     //setvbuf(stdout, NULL, _IONBF, 0);
     //setvbuf(stderr, NULL, _IONBF, 0);
-    
+
     LOGI("commandline args:\n");
     for (int i = 0; i < argc; i++)
         LOGI("  %2d: '%s'\n", i, argv[i]);
@@ -455,7 +454,7 @@
 
 #if 0
     // Hack to keep libc from beating the filesystem to death.  It's
-    // hitting /etc/localtime frequently, 
+    // hitting /etc/localtime frequently,
     //
     // This statement locks us into Pacific time.  We could do better,
     // but there's not much point until we're sure that the library
@@ -467,15 +466,15 @@
 
     /* track our progress through the boot sequence */
     const int LOG_BOOT_PROGRESS_START = 3000;
-    LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, 
+    LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
         ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
 
     validateTime();
 
     proc = ProcessState::self();
-    
+
     boot_init();
-    
+
     /* If we are in multiprocess mode, have zygote spawn the system
      * server process and call system_init(). If we are running in
      * single process mode just call system_init() directly.
@@ -488,8 +487,8 @@
         property_get("log.redirect-stdio", propBuf, "");
         logStdio = (strcmp(propBuf, "true") == 0);
 
-        zygote_run_oneshot((int)(!logStdio), 
-                sizeof(ZYGOTE_ARGV) / sizeof(ZYGOTE_ARGV[0]), 
+        zygote_run_oneshot((int)(!logStdio),
+                sizeof(ZYGOTE_ARGV) / sizeof(ZYGOTE_ARGV[0]),
                 ZYGOTE_ARGV);
 
         //start_process("/system/bin/mediaserver");
@@ -497,7 +496,7 @@
     } else {
 #ifndef HAVE_ANDROID_OS
         QuickRuntime* runt = new QuickRuntime();
-        runt->start("com/android/server/SystemServer", 
+        runt->start("com/android/server/SystemServer",
                     false /* spontaneously fork system server from zygote */);
 #endif
     }
@@ -506,11 +505,11 @@
 
     finish_system_init(proc);
     run(proc);
-    
+
 bail:
     if (proc != NULL) {
         proc->setContextObject(NULL);
     }
-    
+
     return 0;
 }
diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
index a29ba73..b615764 100644
--- a/cmds/system_server/library/system_init.cpp
+++ b/cmds/system_server/library/system_init.cpp
@@ -37,7 +37,7 @@
  * This class is used to kill this process when the runtime dies.
  */
 class GrimReaper : public IBinder::DeathRecipient {
-public: 
+public:
     GrimReaper() { }
 
     virtual void binderDied(const wp<IBinder>& who)
@@ -54,15 +54,15 @@
 extern "C" status_t system_init()
 {
     LOGI("Entered system_init()");
-    
+
     sp<ProcessState> proc(ProcessState::self());
-    
+
     sp<IServiceManager> sm = defaultServiceManager();
     LOGI("ServiceManager: %p\n", sm.get());
-    
+
     sp<GrimReaper> grim = new GrimReaper();
     sm->asBinder()->linkToDeath(grim, grim.get(), 0);
-    
+
     char propBuf[PROPERTY_VALUE_MAX];
     property_get("system_init.startsurfaceflinger", propBuf, "1");
     if (strcmp(propBuf, "1") == 0) {
@@ -97,12 +97,23 @@
     // the beginning of their processes's main(), before calling
     // the init function.
     LOGI("System server: starting Android runtime.\n");
-    
     AndroidRuntime* runtime = AndroidRuntime::getRuntime();
 
     LOGI("System server: starting Android services.\n");
-    runtime->callStatic("com/android/server/SystemServer", "init2");
-        
+    JNIEnv* env = runtime->getJNIEnv();
+    if (env == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    jclass clazz = env->FindClass("com/android/server/SystemServer");
+    if (clazz == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    jmethodID methodId = env->GetStaticMethodID(clazz, "init2", "()V");
+    if (methodId == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    env->CallStaticVoidMethod(clazz, methodId);
+
     // If running in our own process, just go into the thread
     // pool.  Otherwise, call the initialization finished
     // func to let this process continue its initilization.
@@ -114,4 +125,3 @@
     }
     return NO_ERROR;
 }
-
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f29d83e..b628b9d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1,19 +1,18 @@
-/* //device/libs/android_runtime/AndroidRuntime.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 #define LOG_TAG "AndroidRuntime"
 //#define LOG_NDEBUG 0
@@ -212,7 +211,7 @@
     if (value != NULL && strcmp(value, "true") == 0) {
         return 1;
     }
-    
+
     return 0;
 }
 
@@ -227,7 +226,7 @@
     { "isComputerOn", "()I",
         (void*) com_android_internal_os_RuntimeInit_isComputerOn },
     { "turnComputerOn", "()V",
-        (void*) com_android_internal_os_RuntimeInit_turnComputerOn },    
+        (void*) com_android_internal_os_RuntimeInit_turnComputerOn },
     { "getQwertyKeyboard", "()I",
         (void*) com_android_internal_os_RuntimeInit_getQwertyKeyboard },
 };
@@ -278,51 +277,16 @@
     return jniRegisterNativeMethods(env, className, gMethods, numMethods);
 }
 
-/*
- * Call a static Java Programming Language function that takes no arguments and returns void.
- */
-status_t AndroidRuntime::callStatic(const char* className, const char* methodName)
+status_t AndroidRuntime::callMain(const char* className,
+    jclass clazz, int argc, const char* const argv[])
 {
     JNIEnv* env;
-    jclass clazz;
-    jmethodID methodId;
-
-    env = getJNIEnv();
-    if (env == NULL)
-        return UNKNOWN_ERROR;
-
-    clazz = findClass(env, className);
-    if (clazz == NULL) {
-        LOGE("ERROR: could not find class '%s'\n", className);
-        return UNKNOWN_ERROR;
-    }
-    methodId = env->GetStaticMethodID(clazz, methodName, "()V");
-    if (methodId == NULL) {
-        LOGE("ERROR: could not find method %s.%s\n", className, methodName);
-        return UNKNOWN_ERROR;
-    }
-
-    env->CallStaticVoidMethod(clazz, methodId);
-
-    return NO_ERROR;
-}
-
-status_t AndroidRuntime::callMain(
-    const char* className, int argc, const char* const argv[])
-{
-    JNIEnv* env;
-    jclass clazz;
     jmethodID methodId;
 
     LOGD("Calling main entry %s", className);
 
     env = getJNIEnv();
-    if (env == NULL)
-        return UNKNOWN_ERROR;
-
-    clazz = findClass(env, className);
-    if (clazz == NULL) {
-        LOGE("ERROR: could not find class '%s'\n", className);
+    if (clazz == NULL || env == NULL) {
         return UNKNOWN_ERROR;
     }
 
@@ -352,70 +316,6 @@
 }
 
 /*
- * Find the named class.
- */
-jclass AndroidRuntime::findClass(JNIEnv* env, const char* className)
-{
-    if (env->ExceptionCheck()) {
-        LOGE("ERROR: exception pending on entry to findClass()");
-        return NULL;
-    }
-
-    /*
-     * This is a little awkward because the JNI FindClass call uses the
-     * class loader associated with the native method we're executing in.
-     * Because this native method is part of a "boot" class, JNI doesn't
-     * look for the class in CLASSPATH, which unfortunately is a likely
-     * location for it.  (Had we issued the FindClass call before calling
-     * into the VM -- at which point there isn't a native method frame on
-     * the stack -- the VM would have checked CLASSPATH.  We have to do
-     * this because we call into Java Programming Language code and
-     * bounce back out.)
-     *
-     * JNI lacks a "find class in a specific class loader" operation, so we
-     * have to do things the hard way.
-     */
-    jclass cls = NULL;
-
-    jclass javaLangClassLoader;
-    jmethodID getSystemClassLoader, loadClass;
-    jobject systemClassLoader;
-    jstring strClassName;
-
-    /* find the "system" class loader; none of this is expected to fail */
-    javaLangClassLoader = env->FindClass("java/lang/ClassLoader");
-    assert(javaLangClassLoader != NULL);
-    getSystemClassLoader = env->GetStaticMethodID(javaLangClassLoader,
-        "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
-    loadClass = env->GetMethodID(javaLangClassLoader,
-        "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
-    assert(getSystemClassLoader != NULL && loadClass != NULL);
-    systemClassLoader = env->CallStaticObjectMethod(javaLangClassLoader,
-        getSystemClassLoader);
-    assert(systemClassLoader != NULL);
-
-    /* create an object for the class name string; alloc could fail */
-    strClassName = env->NewStringUTF(className);
-    if (env->ExceptionCheck()) {
-        LOGE("ERROR: unable to convert '%s' to string", className);
-        return NULL;
-    }
-    LOGV("system class loader is %p, loading %p (%s)",
-        systemClassLoader, strClassName, className);
-
-    /* try to find the named class */
-    cls = (jclass) env->CallObjectMethod(systemClassLoader, loadClass,
-                        strClassName);
-    if (env->ExceptionCheck()) {
-        LOGE("ERROR: unable to load class '%s' from %p",
-            className, systemClassLoader);
-        return NULL;
-    }
-
-    return cls;
-}
-
-/*
  * The VM calls this through the "exit" hook.
  */
 static void runtime_exit(int code)
@@ -457,7 +357,7 @@
 int AndroidRuntime::addVmArguments(int argc, const char* const argv[])
 {
     int i;
-    
+
     for (i = 0; i<argc; i++) {
         if (argv[i][0] != '-') {
             return i;
@@ -890,6 +790,17 @@
     return result;
 }
 
+char* AndroidRuntime::toSlashClassName(const char* className)
+{
+    char* result = strdup(className);
+    for (char* cp = result; *cp != '\0'; cp++) {
+        if (*cp == '.') {
+            *cp = '/';
+        }
+    }
+    return result;
+}
+
 /*
  * Start the Android runtime.  This involves starting the virtual machine
  * and calling the "static void main(String[] args)" method in the class
@@ -900,20 +811,16 @@
     LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
             className != NULL ? className : "(unknown)");
 
-    char* slashClassName = NULL;
-    char* cp;
-    JNIEnv* env;
-
     blockSigpipe();
 
-    /* 
-     * 'startSystemServer == true' means runtime is obsolete and not run from 
+    /*
+     * 'startSystemServer == true' means runtime is obsolete and not run from
      * init.rc anymore, so we print out the boot start event here.
      */
     if (startSystemServer) {
         /* track our progress through the boot sequence */
         const int LOG_BOOT_PROGRESS_START = 3000;
-        LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, 
+        LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
                        ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
     }
 
@@ -922,7 +829,7 @@
         rootDir = "/system";
         if (!hasDir("/system")) {
             LOG_FATAL("No root directory specified, and /android does not exist.");
-            goto bail;
+            return;
         }
         setenv("ANDROID_ROOT", rootDir, 1);
     }
@@ -931,15 +838,18 @@
     //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
 
     /* start the virtual machine */
-    if (startVm(&mJavaVM, &env) != 0)
-        goto bail;
+    JNIEnv* env;
+    if (startVm(&mJavaVM, &env) != 0) {
+        return;
+    }
+    onVmCreated(env);
 
     /*
      * Register android functions.
      */
     if (startReg(env) < 0) {
         LOGE("Unable to register all android natives\n");
-        goto bail;
+        return;
     }
 
     /*
@@ -959,7 +869,7 @@
     classNameStr = env->NewStringUTF(className);
     assert(classNameStr != NULL);
     env->SetObjectArrayElement(strArray, 0, classNameStr);
-    startSystemServerStr = env->NewStringUTF(startSystemServer ? 
+    startSystemServerStr = env->NewStringUTF(startSystemServer ?
                                                  "true" : "false");
     env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
 
@@ -967,20 +877,13 @@
      * Start VM.  This thread becomes the main thread of the VM, and will
      * not return until the VM exits.
      */
-    jclass startClass;
-    jmethodID startMeth;
-
-    slashClassName = strdup(className);
-    for (cp = slashClassName; *cp != '\0'; cp++)
-        if (*cp == '.')
-            *cp = '/';
-
-    startClass = env->FindClass(slashClassName);
+    char* slashClassName = toSlashClassName(className);
+    jclass startClass = env->FindClass(slashClassName);
     if (startClass == NULL) {
         LOGE("JavaVM unable to locate class '%s'\n", slashClassName);
         /* keep going */
     } else {
-        startMeth = env->GetStaticMethodID(startClass, "main",
+        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
             "([Ljava/lang/String;)V");
         if (startMeth == NULL) {
             LOGE("JavaVM unable to find main() in '%s'\n", className);
@@ -994,15 +897,13 @@
 #endif
         }
     }
+    free(slashClassName);
 
     LOGD("Shutting down VM\n");
     if (mJavaVM->DetachCurrentThread() != JNI_OK)
         LOGW("Warning: unable to detach main thread\n");
     if (mJavaVM->DestroyJavaVM() != 0)
         LOGW("Warning: VM did not shut down cleanly\n");
-
-bail:
-    free(slashClassName);
 }
 
 void AndroidRuntime::start()
@@ -1017,6 +918,11 @@
     exit(code);
 }
 
+void AndroidRuntime::onVmCreated(JNIEnv* env)
+{
+    // If AndroidRuntime had anything to do here, we'd have done it in 'start'.
+}
+
 /*
  * Get the JNIEnv pointer for this thread.
  *
@@ -1111,7 +1017,7 @@
  * into the VM before it really starts executing.
  */
 /*static*/ int AndroidRuntime::javaCreateThreadEtc(
-                                android_thread_func_t entryFunction, 
+                                android_thread_func_t entryFunction,
                                 void* userData,
                                 const char* threadName,
                                 int32_t threadPriority,
@@ -1299,7 +1205,7 @@
     REG_JNI(register_android_backup_BackupDataOutput),
     REG_JNI(register_android_backup_FileBackupHelperBase),
     REG_JNI(register_android_backup_BackupHelperDispatcher),
-    
+
     REG_JNI(register_android_app_ActivityThread),
     REG_JNI(register_android_app_NativeActivity),
     REG_JNI(register_android_view_InputChannel),
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 34f0fdc..e5c28489 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -103,17 +103,6 @@
     }
 }
 
-
-static void fakeProcessEntry(void* arg)
-{
-    String8* cls = (String8*)arg;
-
-    AndroidRuntime* jr = AndroidRuntime::getRuntime();
-    jr->callMain(cls->string(), 0, NULL);
-
-    delete cls;
-}
-
 jint android_os_Process_myPid(JNIEnv* env, jobject clazz)
 {
     return getpid();
diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h
index de2d50b..b02a057 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/include/android_runtime/AndroidRuntime.h
@@ -46,14 +46,10 @@
         const char* className, const JNINativeMethod* gMethods, int numMethods);
 
     /**
-     * Call a static Java function that takes no arguments and returns void.
-     */
-    status_t callStatic(const char* className, const char* methodName);
-
-    /**
      * Call a class's static main method with the given arguments,
      */
-    status_t callMain(const char* className, int argc, const char* const argv[]);
+    status_t callMain(const char* className, jclass clazz, int argc,
+        const char* const argv[]);
 
     /**
      * Find a class, with the input either of the form
@@ -69,6 +65,13 @@
     static AndroidRuntime* getRuntime();
 
     /**
+     * This gets called after the VM has been created, but before we
+     * run any code. Override it to make any FindClass calls that need
+     * to use CLASSPATH.
+     */
+    virtual void onVmCreated(JNIEnv* env);
+
+    /**
      * This gets called after the JavaVM has initialized.  Override it
      * with the system's native entry point.
      */
@@ -98,6 +101,9 @@
     /** return a pointer to the JNIEnv pointer for this thread */
     static JNIEnv* getJNIEnv();
 
+    /** return a new string corresponding to 'className' with all '.'s replaced by '/'s. */
+    static char* toSlashClassName(const char* className);
+
 private:
     static int startReg(JNIEnv* env);
     void parseExtraOpts(char* extraOptsBuf);