| // |
| // Copyright 2005 The Android Open Source Project |
| // |
| // Main entry point for runtime. |
| // |
| |
| #include "ServiceManager.h" |
| #include "SignalHandler.h" |
| |
| #include <utils.h> |
| #include <utils/IPCThreadState.h> |
| #include <utils/ProcessState.h> |
| #include <utils/Log.h> |
| #include <cutils/zygote.h> |
| |
| #include <cutils/properties.h> |
| |
| #include <private/utils/Static.h> |
| |
| #include <ui/ISurfaceComposer.h> |
| |
| #include <android_runtime/AndroidRuntime.h> |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <getopt.h> |
| #include <signal.h> |
| #include <errno.h> |
| #include <sys/stat.h> |
| #include <linux/capability.h> |
| #include <linux/ioctl.h> |
| #ifdef HAVE_ANDROID_OS |
| # include <linux/android_alarm.h> |
| #endif |
| |
| #undef LOG_TAG |
| #define LOG_TAG "runtime" |
| |
| static const char* ZYGOTE_ARGV[] = { |
| "--setuid=1000", |
| "--setgid=1000", |
| "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003", |
| /* CAP_SYS_TTY_CONFIG & CAP_SYS_RESOURCE & CAP_NET_BROADCAST & |
| * CAP_NET_ADMIN & CAP_NET_RAW & CAP_NET_BIND_SERVICE & CAP_KILL & |
| * CAP_SYS_BOOT |
| */ |
| "--capabilities=88161312,88161312", |
| "--runtime-init", |
| "--nice-name=system_server", |
| "com.android.server.SystemServer" |
| }; |
| |
| using namespace android; |
| |
| extern "C" status_t system_init(); |
| |
| enum { |
| SYSTEM_PROCESS_TAG = DEFAULT_PROCESS_TAG+1 |
| }; |
| |
| extern Mutex gEventQMutex; |
| extern Condition gEventQCondition; |
| |
| namespace android { |
| |
| extern status_t app_init(const char* className); |
| 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: |
| GrimReaper() { } |
| |
| virtual void binderDied(const wp<IBinder>& who) |
| { |
| LOGI("Grim Reaper killing runtime..."); |
| kill(getpid(), SIGKILL); |
| } |
| }; |
| |
| extern void QuickTests(); |
| |
| /* |
| * Print usage info. |
| */ |
| static void usage(const char* argv0) |
| { |
| fprintf(stderr, |
| "Usage: runtime [-g gamma] [-l logfile] [-n] [-s]\n" |
| " [-j app-component] [-v app-verb] [-d app-data]\n" |
| "\n" |
| "-l: File to send log messages to\n" |
| "-n: Don't print to stdout/stderr\n" |
| "-s: Force single-process mode\n" |
| "-j: Custom home app component name\n" |
| "-v: Custom home app intent verb\n" |
| "-d: Custom home app intent data\n" |
| ); |
| exit(1); |
| } |
| |
| // Selected application to run. |
| static const char* gInitialApplication = NULL; |
| static const char* gInitialVerb = NULL; |
| static const char* gInitialData = NULL; |
| |
| static void writeStringToParcel(Parcel& parcel, const char* str) |
| { |
| if (str) { |
| parcel.writeString16(String16(str)); |
| } else { |
| parcel.writeString16(NULL, 0); |
| } |
| } |
| |
| /* |
| * Starting point for program logic. |
| * |
| * Returns with an exit status code (0 on success, nonzero on error). |
| */ |
| static int run(sp<ProcessState>& proc) |
| { |
| // Temporary hack to call startRunning() on the activity manager. |
| sp<IServiceManager> sm = defaultServiceManager(); |
| sp<IBinder> am; |
| while ((am = sm->getService(String16("activity"))) == NULL) { |
| LOGI("Waiting for activity manager..."); |
| } |
| Parcel data, reply; |
| // XXX Need to also supply a package name for this to work again. |
| // IActivityManager::getInterfaceDescriptor() is the token for invoking on this interface; |
| // hardcoding it here avoids having to link with the full Activity Manager library |
| data.writeInterfaceToken(String16("android.app.IActivityManager")); |
| writeStringToParcel(data, NULL); |
| writeStringToParcel(data, gInitialApplication); |
| writeStringToParcel(data, gInitialVerb); |
| writeStringToParcel(data, gInitialData); |
| LOGI("run() sending FIRST_CALL_TRANSACTION to activity manager"); |
| am->transact(IBinder::FIRST_CALL_TRANSACTION, data, &reply); |
| |
| if (proc->supportsProcesses()) { |
| // Now we link to the Activity Manager waiting for it to die. If it does kill ourself. |
| // initd will restart this process and bring the system back up. |
| sp<GrimReaper> grim = new GrimReaper(); |
| am->linkToDeath(grim, grim.get(), 0); |
| |
| // Now join the thread pool. Note this is needed so that the message enqueued in the driver |
| // for the linkToDeath gets processed. |
| IPCThreadState::self()->joinThreadPool(); |
| } else { |
| // Keep this thread running forever... |
| while (1) { |
| usleep(100000); |
| } |
| } |
| return 1; |
| } |
| |
| |
| }; // namespace android |
| |
| |
| /* |
| * 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 |
| * running in its own process, which doesn't return until the runtime is |
| * being shut down. So it will call back to here from inside of Dalvik, |
| * to allow us to continue booting up. |
| */ |
| static void finish_system_init(sp<ProcessState>& proc) |
| { |
| // If we are running multiprocess, we now need to have the |
| // thread pool started here. We don't do this in boot_init() |
| // because when running single process we need to start the |
| // thread pool after the Android runtime has been started (so |
| // the pool uses Dalvik threads). |
| if (proc->supportsProcesses()) { |
| proc->startThreadPool(); |
| } |
| } |
| |
| |
| // This function can be used to enforce security to different |
| // root contexts. For now, we just give every access. |
| static bool contextChecker( |
| const String16& name, const sp<IBinder>& caller, void* userData) |
| { |
| return true; |
| } |
| |
| /* |
| * Initialization of boot services. |
| * |
| * This is where we perform initialization of all of our low-level |
| * boot services. Most importantly, here we become the context |
| * manager and use that to publish the service manager that will provide |
| * access to all other services. |
| */ |
| 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); |
| } |
| |
| /* |
| * Redirect stdin/stdout/stderr to /dev/null. |
| */ |
| static void redirectStdFds(void) |
| { |
| int fd = open("/dev/null", O_RDWR, 0); |
| if (fd < 0) { |
| LOGW("Unable to open /dev/null: %s\n", strerror(errno)); |
| } else { |
| dup2(fd, 0); |
| dup2(fd, 1); |
| dup2(fd, 2); |
| close(fd); |
| } |
| } |
| |
| static int hasDir(const char* dir) |
| { |
| struct stat s; |
| int res = stat(dir, &s); |
| if (res == 0) { |
| return S_ISDIR(s.st_mode); |
| } |
| return 0; |
| } |
| |
| static void validateTime() |
| { |
| #if HAVE_ANDROID_OS |
| int fd; |
| 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)); |
| return; |
| } |
| res = ioctl(fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_RTC_WAKEUP), &ts); |
| if(res < 0) { |
| LOGW("Unable to read rtc, %s\n", strerror(errno)); |
| } |
| else if(ts.tv_sec >= min_time) { |
| goto done; |
| } |
| LOGW("Invalid time detected, %ld set to %ld\n", ts.tv_sec, min_time); |
| ts.tv_sec = min_time; |
| ts.tv_nsec = 0; |
| res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); |
| if(res < 0) { |
| LOGW("Unable to set rtc to %ld: %s\n", ts.tv_sec, strerror(errno)); |
| } |
| done: |
| close(fd); |
| #endif |
| } |
| |
| #ifndef HAVE_ANDROID_OS |
| class QuickRuntime : public AndroidRuntime |
| { |
| public: |
| QuickRuntime() {} |
| |
| virtual void onStarted() |
| { |
| printf("QuickRuntime: onStarted\n"); |
| } |
| }; |
| #endif |
| |
| static status_t start_process(const char* name); |
| |
| static void restart_me(pid_t child, void* userData) |
| { |
| start_process((const char*)userData); |
| } |
| |
| static status_t start_process(const char* name) |
| { |
| String8 path(name); |
| Vector<const char*> args; |
| String8 leaf(path.getPathLeaf()); |
| String8 parentDir(path.getPathDir()); |
| args.insertAt(leaf.string(), 0); |
| args.add(parentDir.string()); |
| args.add(NULL); |
| pid_t child = fork(); |
| if (child < 0) { |
| status_t err = errno; |
| LOGE("*** fork of child %s failed: %s", leaf.string(), strerror(err)); |
| return -errno; |
| } else if (child == 0) { |
| LOGI("Executing: %s", path.string()); |
| execv(path.string(), const_cast<char**>(args.array())); |
| int err = errno; |
| LOGE("Exec failed: %s\n", strerror(err)); |
| _exit(err); |
| } else { |
| SignalHandler::setChildHandler(child, DEFAULT_PROCESS_TAG, |
| restart_me, (void*)name); |
| } |
| return -errno; |
| } |
| |
| /* |
| * Application entry point. |
| * |
| * Parse arguments, set some values, and pass control off to Run(). |
| * |
| * This is redefined to "SDL_main" on SDL simulator builds, and |
| * "runtime_main" on wxWidgets builds. |
| */ |
| extern "C" |
| int main(int argc, char* const argv[]) |
| { |
| bool singleProcess = false; |
| const char* logFile = NULL; |
| 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]); |
| #endif |
| |
| while (1) { |
| ic = getopt(argc, argv, "g:j:v:d:l:ns"); |
| if (ic < 0) |
| break; |
| |
| switch (ic) { |
| case 'g': |
| break; |
| case 'j': |
| gInitialApplication = optarg; |
| break; |
| case 'v': |
| gInitialVerb = optarg; |
| break; |
| case 'd': |
| gInitialData = optarg; |
| break; |
| case 'l': |
| logFile = optarg; |
| break; |
| case 'n': |
| redirectStdFds(); |
| break; |
| case 's': |
| singleProcess = true; |
| break; |
| case '?': |
| default: |
| LOGE("runtime: unrecognized flag -%c\n", ic); |
| usage(argv[0]); |
| break; |
| } |
| } |
| if (optind < argc) { |
| LOGE("runtime: extra stuff: %s\n", argv[optind]); |
| usage(argv[0]); |
| } |
| |
| if (singleProcess) { |
| ProcessState::setSingleProcess(true); |
| } |
| |
| if (logFile != NULL) { |
| android_logToFile(NULL, logFile); |
| } |
| |
| /* |
| * Set up ANDROID_* environment variables. |
| * |
| * TODO: the use of $ANDROID_PRODUCT_OUT will go away soon. |
| */ |
| static const char* kSystemDir = "/system"; |
| static const char* kDataDir = "/data"; |
| static const char* kAppSubdir = "/app"; |
| const char* out = NULL; |
| #ifndef HAVE_ANDROID_OS |
| //out = getenv("ANDROID_PRODUCT_OUT"); |
| #endif |
| if (out == NULL) |
| out = ""; |
| |
| char* systemDir = (char*) malloc(strlen(out) + strlen(kSystemDir) +1); |
| char* dataDir = (char*) malloc(strlen(out) + strlen(kDataDir) +1); |
| |
| sprintf(systemDir, "%s%s", out, kSystemDir); |
| sprintf(dataDir, "%s%s", out, kDataDir); |
| setenv("ANDROID_ROOT", systemDir, 1); |
| setenv("ANDROID_DATA", dataDir, 1); |
| |
| char* assetDir = (char*) malloc(strlen(systemDir) + strlen(kAppSubdir) +1); |
| sprintf(assetDir, "%s%s", systemDir, kAppSubdir); |
| |
| LOGI("Startup: sys='%s' asset='%s' data='%s'\n", |
| systemDir, assetDir, dataDir); |
| free(systemDir); |
| free(dataDir); |
| |
| #ifdef HAVE_ANDROID_OS |
| /* set up a process group for easier killing on the device */ |
| setpgid(0, getpid()); |
| #endif |
| |
| // Change to asset dir. This is only necessary if we've changed to |
| // a different directory, but there's little harm in doing it regardless. |
| // |
| // Expecting assets to live in the current dir is not a great idea, |
| // because some of our code or one of our libraries could change the |
| // directory out from under us. Preserve the behavior for now. |
| if (chdir(assetDir) != 0) { |
| LOGW("WARNING: could not change dir to '%s': %s\n", |
| assetDir, strerror(errno)); |
| } |
| free(assetDir); |
| |
| #if 0 |
| // Hack to keep libc from beating the filesystem to death. It's |
| // 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 |
| // can't be changed to do more along the lines of what we want. |
| #ifndef XP_WIN |
| setenv("TZ", "PST+8PDT,M4.1.0/2,M10.5.0/2", true); |
| #endif |
| #endif |
| |
| /* track our progress through the boot sequence */ |
| const int LOG_BOOT_PROGRESS_START = 3000; |
| 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. |
| */ |
| if (proc->supportsProcesses()) { |
| // If stdio logging is on, system_server should not inherit our stdio |
| // The dalvikvm instance will copy stdio to the log on its own |
| char propBuf[PROPERTY_VALUE_MAX]; |
| bool logStdio = false; |
| property_get("log.redirect-stdio", propBuf, ""); |
| logStdio = (strcmp(propBuf, "true") == 0); |
| |
| zygote_run_oneshot((int)(!logStdio), |
| sizeof(ZYGOTE_ARGV) / sizeof(ZYGOTE_ARGV[0]), |
| ZYGOTE_ARGV); |
| |
| //start_process("/system/bin/mediaserver"); |
| |
| } else { |
| #ifndef HAVE_ANDROID_OS |
| QuickRuntime* runt = new QuickRuntime(); |
| runt->start("com/android/server/SystemServer", |
| false /* spontaneously fork system server from zygote */); |
| #endif |
| } |
| |
| //printf("+++ post-zygote\n"); |
| |
| finish_system_init(proc); |
| run(proc); |
| |
| bail: |
| if (proc != NULL) { |
| proc->setContextObject(NULL); |
| } |
| |
| return 0; |
| } |