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
This commit is contained in:
Elliott Hughes
2011-04-13 15:39:37 -07:00
parent 966f9e558d
commit d195e5ab40
6 changed files with 145 additions and 214 deletions

View File

@@ -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 @@ void app_usage()
"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 @@ public:
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 @@ public:
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 @@ public:
if (proc->supportsProcesses()) {
LOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
}
}
virtual void onExit(int code)
@@ -96,9 +116,10 @@ public:
AndroidRuntime::onExit(code);
}
const char* mParentDir;
const char* mClassName;
jclass mClass;
int mArgC;
const char* const* mArgV;
};
@@ -120,7 +141,7 @@ int main(int argc, const char* const argv[])
// 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 @@ int main(int argc, const char* const argv[])
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 @@ int main(int argc, const char* const argv[])
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");

View File

@@ -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 @@ extern Condition gEventQCondition;
namespace android {
extern status_t app_init(const char* className);
extern void set_finish_init_func(void (*func)());
@@ -76,7 +75,7 @@ extern void set_finish_init_func(void (*func)());
* 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 @@ LOGI("run() sending FIRST_CALL_TRANSACTION to activity manager");
/*
* 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 bool contextChecker(
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 @@ static void validateTime()
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 main(int argc, char* const argv[])
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 @@ int main(int argc, char* const argv[])
#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 @@ int main(int argc, char* const argv[])
/* 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 @@ int main(int argc, char* const argv[])
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 @@ int main(int argc, char* const argv[])
} 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 @@ int main(int argc, char* const argv[])
finish_system_init(proc);
run(proc);
bail:
if (proc != NULL) {
proc->setContextObject(NULL);
}
return 0;
}

View File

@@ -37,7 +37,7 @@ namespace android {
* 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 @@ public:
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 @@ extern "C" status_t system_init()
// 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 @@ extern "C" status_t system_init()
}
return NO_ERROR;
}

View File

@@ -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 @@ static jint com_android_internal_os_RuntimeInit_getQwertyKeyboard(JNIEnv* env, j
if (value != NULL && strcmp(value, "true") == 0) {
return 1;
}
return 0;
}
@@ -227,7 +226,7 @@ static JNINativeMethod gMethods[] = {
{ "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 @@ AndroidRuntime::~AndroidRuntime()
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;
}
@@ -351,70 +315,6 @@ status_t AndroidRuntime::callMain(
return NO_ERROR;
}
/*
* 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.
*/
@@ -457,7 +357,7 @@ static bool runtime_isSensitiveThread() {
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 @@ bail:
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 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
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 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
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 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
//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 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
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 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
* 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 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
#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 @@ void AndroidRuntime::onExit(int code)
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 @@ static int javaDetachThread(void)
* 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 @@ static const RegJNIRec gRegJNI[] = {
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),

View File

@@ -103,17 +103,6 @@ static void signalExceptionForGroupError(JNIEnv* env, jobject obj, int err)
}
}
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();

View File

@@ -45,15 +45,11 @@ public:
static int registerNativeMethods(JNIEnv* env,
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
@@ -68,6 +64,13 @@ public:
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 @@ public:
/** 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);