blob: cee5e8bada96a7c632301df19553fa0049b01c23 [file] [log] [blame]
/*
* Main entry of app process.
*
* Starts the interpreted runtime, then starts up the application.
*
*/
#define LOG_TAG "appproc"
#include "stringprintf.h"
#include "logging.h"
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <cutils/process_name.h>
#include <cutils/memory.h>
#include <android_runtime/AndroidRuntime.h>
#include <stdio.h>
#include <unistd.h>
namespace android {
void app_usage()
{
fprintf(stderr,
"Usage: oat_process [java-options] cmd-dir start-class-name [options]\n");
}
class AppRuntime : public AndroidRuntime
{
public:
AppRuntime()
: mParentDir(NULL)
, mClassName(NULL)
, mClass(NULL)
, mArgC(0)
, mArgV(NULL)
{
}
#if 0
// this appears to be unused
const char* getParentDir() const
{
return mParentDir;
}
#endif
const char* getClassName() const
{
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) {
LOG(FATAL) << "Could not find class: " << mClassName;
}
free(slashClassName);
mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
}
virtual void onStarted()
{
sp<ProcessState> proc = ProcessState::self();
LOGV("App process: starting thread pool.\n");
proc->startThreadPool();
AndroidRuntime* ar = AndroidRuntime::getRuntime();
ar->callMain(mClassName, mClass, mArgC, mArgV);
IPCThreadState::self()->stopProcess();
}
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
LOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
virtual void onExit(int code)
{
if (mClassName == NULL) {
// if zygote
IPCThreadState::self()->stopProcess();
}
AndroidRuntime::onExit(code);
}
const char* mParentDir;
const char* mClassName;
jclass mClass;
int mArgC;
const char* const* mArgV;
};
}
using namespace android;
/*
* sets argv0 to as much of newArgv0 as will fit
*/
static void setArgv0(const char *argv0, const char *newArgv0)
{
strlcpy(const_cast<char *>(argv0), newArgv0, strlen(argv0));
}
int main(int argc, const char* 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;
}
mArgLen--;
AppRuntime runtime;
const char* argv0 = argv[0];
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
// ignore /system/bin/app_process when invoked via WrapperInit
if (strcmp(argv[0], "/system/bin/app_process") == 0) {
LOG(INFO) << "Removing /system/bin/app_process argument";
argc--;
argv++;
for (int i = 0; i < argc; i++) {
LOG(INFO) << StringPrintf("argv[%d]=%s", i, argv[i]);
}
}
// TODO: remove when we default the boot image
int oatArgc = argc + 1;
const char* oatArgv[oatArgc];
if (strcmp(argv[0], "-Ximage:/data/art-cache/boot.art") != 0) {
LOG(INFO) << "Adding image arguments";
oatArgv[0] = "-Ximage:/data/art-cache/boot.art";
memcpy(oatArgv + (oatArgc - argc), argv, argc * sizeof(*argv));
argv = oatArgv;
argc = oatArgc;
for (int i = 0; i < argc; i++) {
LOG(INFO) << StringPrintf("argv[%d]=%s", i, argv[i]);
}
}
// TODO: remove the heap arguments when implicit garbage collection enabled
LOG(INFO) << "Adding heap arguments";
int heapArgc = argc + 2;
const char* heapArgv[heapArgc];
heapArgv[0] = "-Xms64m";
heapArgv[1] = "-Xmx64m";
memcpy(heapArgv + (heapArgc - argc), argv, argc * sizeof(*argv));
argv = heapArgv;
argc = heapArgc;
for (int i = 0; i < argc; i++) {
LOG(INFO) << StringPrintf("argv[%d]=%s", i, argv[i]);
}
// TODO: change the system default to not perform preloading
LOG(INFO) << "Disabling preloading";
for (int i = 0; i < argc; i++) {
if (strcmp(argv[i], "preload") == 0) {
argv[i] = "nopreload";
break;
}
}
for (int i = 0; i < argc; i++) {
LOG(INFO) << StringPrintf("argv[%d]=%s", i, argv[i]);
}
// Everything up to '--' or first non '-' arg goes to the vm
int i = runtime.addVmArguments(argc, argv);
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool noPreload = false;
bool application = false;
const char* parentDir = NULL;
const char* niceName = NULL;
const char* className = NULL;
while (i < argc) {
const char* arg = argv[i++];
if (!parentDir) {
parentDir = arg;
} else if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = "zygote";
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--no-preload") == 0) {
noPreload = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName = arg + 12;
} else {
className = arg;
break;
}
}
if (niceName && *niceName) {
setArgv0(argv0, niceName);
set_process_name(niceName);
}
runtime.mParentDir = parentDir;
if (zygote) {
std::string options;
if (startSystemServer) {
options += "start-system-server ";
}
if (noPreload) {
options += "no-preload ";
}
runtime.start("com.android.internal.os.ZygoteInit", options.c_str());
} else if (className) {
// Remainder of args get passed to startup class main()
runtime.mClassName = className;
runtime.mArgC = argc - i;
runtime.mArgV = argv + i;
runtime.start("com.android.internal.os.RuntimeInit",
application ? "application" : "tool");
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG(FATAL) << "oat_process: no class name or --zygote supplied.";
return 10;
}
}