// // Copyright 2005 The Android Open Source Project // // Management of the simulated device. // // For compilers that support precompilation, include "wx/wx.h". #include "wx/wxprec.h" // Otherwise, include all standard headers #ifndef WX_PRECOMP # include "wx/wx.h" #endif #include "wx/image.h" #include "DeviceManager.h" #include "MyApp.h" #include "DeviceWindow.h" #include "LogWindow.h" #include "UserEvent.h" #include "UserEventMessage.h" #include "SimRuntime.h" #include "utils.h" #include <unistd.h> #include <signal.h> #include <errno.h> #if !defined(SIGKILL) // doesn't exist in MinGW # if defined(SIGBREAK) # define SIGKILL SIGBREAK // intended for Ctrl-Break # else # define SIGKILL SIGABRT # endif #endif /* * Constructor. */ DeviceManager::DeviceManager(void) : mThread(NULL), mDisplay(NULL), mNumDisplays(0), mKeyMap(NULL), mpStatusWindow(NULL) { //printf("--- DeviceManager constructor\n"); } /* * Destructor. Snuff the thread if it's still kicking. */ DeviceManager::~DeviceManager(void) { //printf("--- DeviceManager destructor\n"); if (mThread != NULL && mThread->IsRunning()) { mThread->KillChildProcesses(); } if (mThread != NULL) { wxThread::ExitCode code; printf("Sim: Waiting for old runtime thread..."); fflush(stdout); code = mThread->Wait(); // join the old thread printf("done (code=%ld)\n", (long) code); } delete mThread; mThread = NULL; delete[] mDisplay; free((void*)mKeyMap); } /* * Initialize the device configuration. * * "statusWindow" is where message boxes with failure messages go, usually * the main frame. */ bool DeviceManager::Init(int numDisplays, wxWindow* statusWindow) { //if (IsRunning()) { // fprintf(stderr, "ERROR: tried to Configure device while running\n"); // return false; //} assert(mDisplay == NULL); assert(numDisplays > 0); //if (mDisplay != NULL) // delete[] mDisplay; mDisplay = new Display[numDisplays]; mNumDisplays = numDisplays; mpStatusWindow = statusWindow; return true; } /* * Have we been initialized already? */ bool DeviceManager::IsInitialized(void) const { return (mDisplay != NULL); } #if 0 /* * Return the Nth display. */ int DeviceManager::GetShmemKey(int displayIndex) { assert(displayIndex >= 0 && displayIndex < mNumDisplays); return mDisplay[displayIndex].GetShmemKey(); } #endif /* * Define mapping between the device's display and a wxWidgets window. */ bool DeviceManager::SetDisplayConfig(int displayIndex, wxWindow* window, int width, int height, android::PixelFormat format, int refresh) { assert(displayIndex >= 0 && displayIndex < mNumDisplays); if (!mDisplay[displayIndex].Create(displayIndex, window, width, height, format, refresh)) { fprintf(stderr, "Sim: ERROR: unable to configure display %d\n", displayIndex); return false; } else { printf("Sim: configured display %d (w=%d h=%d f=%d re=%d)\n", displayIndex, width, height, format, refresh); return true; } } /* * Define the keyboard */ bool DeviceManager::SetKeyboardConfig(const char *keymap) { free((void*)mKeyMap); mKeyMap = strdup(keymap); return true; } /* * Called before the phone window dialog destroys itself. The goal here * is to prevent the runtime thread from trying to draw after the phone * window has closed for business but before the device manager destructor * gets called. */ void DeviceManager::WindowsClosing(void) { int i; for (i = 0; i < mNumDisplays; i++) mDisplay[i].Uncreate(); } /* * Launch a new runtime process. If there is an existing device manager * thread, we assume that it is in the process of shutting down. */ bool DeviceManager::StartRuntime(void) { return DeviceManager::DeviceThread::LaunchProcess(mpStatusWindow); } /* * Start the runtime management thread when a runtime connects to us. If * there is an existing thread, we assume that it is in the process of * shutting down. */ bool DeviceManager::StartRuntime(android::Pipe* reader, android::Pipe* writer) { if (mThread != NULL) { wxThread::ExitCode code; if (mThread->IsRunning()) { fprintf(stderr, "Sim: ERROR: start requested, but thread running\n"); return false; } // clean up old thread printf("Sim: Waiting for old runtime thread..."); fflush(stdout); code = mThread->Wait(); // join the old thread printf("done (code=%ld)\n", (long) code); delete mThread; mThread = NULL; } assert(mpStatusWindow != NULL); mThread = new DeviceThread(this, mpStatusWindow, reader, writer); if (mThread->Create() != wxTHREAD_NO_ERROR) { fprintf(stderr, "Sim: ERROR: can't create thread\n"); return false; } mThread->Run(); return true; } /* * Get the message stream. Returns NULL if it doesn't exist. */ android::MessageStream* DeviceManager::GetStream(void) { if (!IsRunning()) { fprintf(stderr, "Sim: ERROR: runtime thread not active\n"); return NULL; } assert(mThread != NULL); android::MessageStream* pStream = mThread->GetStream(); assert(pStream != NULL); if (!pStream->isReady()) { fprintf(stderr, "Sim: NOTE: connection to runtime not ready\n"); return NULL; } return pStream; } /* * Stop the runtime, politely. * * We don't clean up the thread here, because it might not exit immediately. */ bool DeviceManager::StopRuntime(void) { android::MessageStream* pStream = GetStream(); if (pStream == NULL) return false; printf("Sim: Sending quit command\n"); android::Message msg; msg.setCommand(android::Simulator::kCommandQuit, 0); pStream->send(&msg); return true; } /* * Kill the runtime as efficiently as possible. */ void DeviceManager::KillRuntime(void) { if (mThread != NULL && mThread->IsRunning()) mThread->KillChildProcesses(); } #if 0 /* * Check if the modified time is newer than mLastModified */ bool DeviceManager::RefreshRuntime(void) { return (IsRunning() && mThread->IsRuntimeNew()); } /* * Tells the device manager that the user does not want to update * the runtime */ void DeviceManager::UserCancelledRefresh(void) { mThread->UpdateLastModified(); } #endif /* * Send an event to the runtime. * * The events are defined in display_device.h. */ void DeviceManager::SendKeyEvent(KeyCode keyCode, bool down) { android::MessageStream* pStream = GetStream(); if (pStream == NULL) return; int event = down ? android::Simulator::kCommandKeyDown : android::Simulator::kCommandKeyUp; //printf("Sim: sending key-%s %d\n", down ? "down" : "up", keyCode); android::Message msg; msg.setCommand(event, keyCode); pStream->send(&msg); } /* * Send a "touch screen" event to the runtime. * * "mode" can be "down" (we're pressing), "up" (we're lifting our finger * off) or "drag". */ void DeviceManager::SendTouchEvent(android::Simulator::TouchMode mode, int x, int y) { android::MessageStream* pStream = GetStream(); if (pStream == NULL) return; //printf("Sim: sending touch-%d x=%d y=%d\n", (int) mode, x, y); android::Message msg; msg.setCommandExt(android::Simulator::kCommandTouch, mode, x, y); pStream->send(&msg); } /* * The runtime has sent us a new frame of stuff to display. * * NOTE: we're still in the runtime management thread. We have to pass the * bitmap through AddPendingEvent to get it over to the main thread. * * We have to make a copy of the data from the runtime; the easiest * way to do that is to convert it to a bitmap here. However, X11 gets * all worked up about calls being made from multiple threads, so we're * better off just copying it into a buffer. * * Because we're decoupled from the runtime, there is a chance that we * could drop frames. Buffering them up is probably worse, since it * creates the possibility that we could stall and run out of memory. * We could save a copy by handing the runtime a pointer to our buffer, * but then we'd have to mutex the runtime against the simulator window * Paint function. */ void DeviceManager::ShowFrame(int displayIndex) { assert(displayIndex >= 0 && displayIndex < mNumDisplays); // copy the data to local storage and convert mDisplay[displayIndex].CopyFromShared(); // create a user event and send it to the window UserEvent uev(0, (void*) displayIndex); wxWindow* pEventWindow = mDisplay[displayIndex].GetWindow(); if (pEventWindow != NULL) { //printf("runtime has image, passing up\n"); pEventWindow->AddPendingEvent(uev); } else { fprintf(stderr, "NOTE: runtime has image, display not available\n"); } } void DeviceManager::Vibrate(int vibrateOn) { ((MyApp*)wxTheApp)->Vibrate(vibrateOn); } /* * Get the display data from the specified display. */ wxBitmap* DeviceManager::GetImageData(int displayIndex) { assert(displayIndex >= 0 && displayIndex < mNumDisplays); return mDisplay[displayIndex].GetImageData(); } /* * Send an event to all device windows */ void DeviceManager::BroadcastEvent(UserEvent& userEvent) { int numDisplays = GetNumDisplays(); for (int i = 0; i < numDisplays; i++) { wxWindow* pEventWindow = mDisplay[i].GetWindow(); if (pEventWindow != NULL) { pEventWindow->AddPendingEvent(userEvent); } } } /* * =========================================================================== * DeviceManager::Display * =========================================================================== */ /* * Fill out the various interesting fields based on the parameters. */ bool DeviceManager::Display::Create(int displayNum, wxWindow* window, int width, int height, android::PixelFormat format, int refresh) { //printf("DeviceManager::Display constructor\n"); assert(window != NULL); if (mImageData != NULL) { assert(false); // no re-init return false; } mDisplayNum = displayNum; mDisplayWindow = window; mWidth = width; mHeight = height; mFormat = format; mRefresh = refresh; // use a fixed key for now mShmemKey = GenerateKey(displayNum); // allocate 24bpp for now mpShmem = new android::Shmem; if (!mpShmem->create(mShmemKey, width * height * 3, true)) return false; //printf("--- CREATED shmem, key=0x%08x addr=%p\n", // mShmemKey, mpShmem->getAddr()); mImageData = new unsigned char[width * height * 3]; if (mImageData == NULL) return false; return true; } /* * The UI components are starting to shut down. We need to do away with * our wxWindow pointer so that the runtime management thread doesn't try * to send it display update events. * * We also need to let go of our side of the shared memory, because a * new DeviceManager may get started up before our destructor gets called, * and we may be re-using the key. */ void DeviceManager::Display::Uncreate(void) { wxMutexLocker locker(mImageDataLock); //printf("--- Uncreate\n"); mDisplayWindow = NULL; // the "locker" mutex keeps this from hosing CopyFromShared() if (mpShmem != NULL) { //printf("--- DELETING shmem, addr=%p\n", mpShmem->getAddr()); delete mpShmem; mpShmem = NULL; } } /* * Make a local copy of the image data. The UI grabs this data from a * different thread, so we have to mutex it. */ void DeviceManager::Display::CopyFromShared(void) { wxMutexLocker locker(mImageDataLock); if (mpShmem == NULL) { //printf("Sim: SKIP CopyFromShared\n"); return; } //printf("Display %d: copying data from %p to %p\n", // mDisplayNum, mpShmem->getAddr(), mImageData); /* data is always 24bpp RGB */ mpShmem->lock(); // avoid tearing memcpy(mImageData, mpShmem->getAddr(), mWidth * mHeight * 3); mpShmem->unlock(); } /* * Get the image data in the form of a newly-allocated bitmap. * * This MUST be called from the UI thread. Creating wxBitmaps in the * runtime management thread will cause X11 failures (e.g. * "Xlib: unexpected async reply"). */ wxBitmap* DeviceManager::Display::GetImageData(void) { wxMutexLocker locker(mImageDataLock); assert(mImageData != NULL); //printf("HEY: creating tmpImage, w=%d h=%d data=%p\n", // mWidth, mHeight, mImageData); /* create a temporary wxImage; it does not own the data */ wxImage tmpImage(mWidth, mHeight, (unsigned char*) mImageData, true); /* return a new bitmap with the converted-for-display data */ return new wxBitmap(tmpImage); } /* * =========================================================================== * DeviceManager::DeviceThread * =========================================================================== */ /* * Some notes on process management under Linux/Mac OS X: * * We want to put the runtime into its own process group. That way we * can send SIGKILL to the entire group to guarantee that we kill it and * all of its children. Simply killing the sim's direct descendant doesn't * do what we want. If it's a debugger, we will just orphan the runtime * without killing it. Even if the runtime is our child, the children of * the runtime might outlive it. * * We want to be able to run the child under GDB or Valgrind, both * of which take input from the tty. They need to be in the "foreground" * process group. We might be debugging or valgrinding the simulator, * or operating in a command-line-only "headless" mode, so in that case * the sim front-end should actually be in the foreground group. * * Putting the runtime in the background group means it can't read input * from the tty (not an issue) and will generate SIGTTOU signals when it * writes output to the tty (easy to ignore). The trick, then, is to * have the simulator and gdb/valgrind in the foreground pgrp while the * runtime itself is in a different group. This group needs to be known * to the simulator so that it can send signals to the appropriate place. * * The solution is to have the runtime process change its process group * after it starts but before it creates any new processes, and then send * the process group ID back to the simulator. The sim can then send * signals to the pgrp to ensure that we don't end up with zombies. Any * "pre-launch" processes, like GDB, stay in the sim's pgrp. This also * allows a consistent API for platforms that don't have fork/exec * (e.g. MinGW). * * This doesn't help us with interactive valgrind (e.g. --db-attach=yes), * because valgrind is an LD_PRELOAD shared library rather than a * separate process. For that, we actually need to use termios(3) to * change the terminal's pgrp, or the interactive stuff just doesn't work. * We don't want to do that every time or attempting to debug the simulator * front-end will have difficulties. * * Making this even more entertaining is the fact that the simulator * front-end could itself be launched in the background. It's essential * that we be careful about assigning a process group to the foreground, * and that we don't restore ourselves unless we were in the foreground to * begin with. * * * Some notes on process management under Windows (Cygwin, MinGW): * * Signals cannot be caught or ignored under MinGW. All signals are fatal. * * Signals can be ignored under Cygwin, but not caught. * * Windows has some process group stuff (e.g. CREATE_NEW_PROCESS_GROUP flag * and GenerateConsoleCtrlEvent()). Need to explore. * * * UPDATE: we've abandoned Mac OS and MinGW, so we now launch the runtime in * a separate xterm. This avoids all tty work on our side. We still need * to learn the pgrp from the child during the initial communication * handshake so we can do necessary cleanup. */ /* * Convert a space-delimited string into an argument vector. * * "arg" is the current arg offset. */ static int stringToArgv(char* mangle, const char** argv, int arg, int maxArgs) { bool first = true; while (*mangle != '\0') { assert(arg < maxArgs); if (first) { argv[arg++] = mangle; first = false; } if (*mangle == ' ') { *mangle = '\0'; first = true; } mangle++; } return arg; } /* * Launch the runtime process in its own terminal window. Start by setting * up the argument vector to the runtime process. * * The last entry in the vector will be a NULL pointer. * * This is awkward and annoying because the wxWidgets strings are current * configured for UNICODE. */ /*static*/ bool DeviceManager::DeviceThread::LaunchProcess(wxWindow* statusWindow) { static const char* kLaunchWrapper = "launch-wrapper"; const int kMaxArgs = 64; Preferences* pPrefs; wxString errMsg; wxString runtimeExe; wxString debuggerExe; wxString debuggerScript; wxString valgrinderExe; wxString launchWrapperExe; wxString launchWrapperArgs; wxString javaAppName; wxString termCmd; wxString tmpStr; char gammaVal[8]; //bool bval; double dval; bool result = false; bool doDebug, doValgrind, doCheckJni, doEnableSound, doEnableFakeCamera; const char** argv = NULL; int arg; wxCharBuffer runtimeExeTmp; wxCharBuffer debuggerExeTmp; wxCharBuffer debuggerScriptTmp; wxCharBuffer javaAppNameTmp; wxCharBuffer valgrinderExeTmp; wxCharBuffer termCmdTmp; wxCharBuffer launchWrapperExeTmp; wxCharBuffer launchWrapperArgsTmp; pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); if (pPrefs == NULL) { errMsg = wxT("Preferences were not loaded."); goto bail; } /* * Set environment variables. This stuff should be passed through as * arguments, but the runtime binary currently has a disconnect * between main() and the VM initilization. * * TODO: remove this in favor of system properties */ #if 0 // TODO: restore this doCheckJni = false; pPrefs->GetBool("check-jni", &doCheckJni); #endif tmpStr.Empty(); pPrefs->GetString("ld-assume-kernel", /*ref*/ tmpStr); if (tmpStr.IsEmpty()) { unsetenv("LD_ASSUME_KERNEL"); } else { setenv("LD_ASSUME_KERNEL", tmpStr.ToAscii(), 1); } doEnableSound = false; pPrefs->GetBool("enable-sound", &doEnableSound); if (doEnableSound) setenv("ANDROIDSOUND", "1", 1); doEnableFakeCamera = false; pPrefs->GetBool("enable-fake-camera", &doEnableFakeCamera); if (doEnableFakeCamera) setenv("ANDROIDFAKECAMERA", "1", 1); /* * Set the Dalvik bootstrap class path. Normally this is set by "init". */ setenv("BOOTCLASSPATH", "/system/framework/core.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar", 1); /* * Figure out where the "runtime" binary lives. */ runtimeExe = ((MyApp*)wxTheApp)->GetRuntimeExe(); assert(!runtimeExe.IsEmpty()); //UpdateLastModified(); /* * Initialize argv. */ argv = new const char*[kMaxArgs]; if (argv == NULL) goto bail; arg = 0; /* * We want to launch the runtime in its own terminal window so we don't * have to fight over who gets access to the controlling tty. We allow * the user to specify the command they want to use to perform the * launch. Here we cut it into pieces for argv. * * To make life easier here, we require that the launch command be * all one piece, i.e. it's not "xterm -e <stuff> -geom blah" with our * stuff in the middle. */ termCmd.Empty(); pPrefs->GetString("launch-command", /*ref*/ termCmd); if (termCmd.IsEmpty()) { fprintf(stderr, "Sim: WARNING: launch-command is empty\n"); } else { termCmdTmp = termCmd.ToAscii(); char* mangle = strdup(termCmdTmp); arg = stringToArgv(mangle, argv, arg, kMaxArgs); } /* * The "launch-wrapper" binary lives in the same place as the runtime. * This sets up LD_PRELOAD and some other environment variables. */ int charIdx; charIdx = runtimeExe.Find('/', true); if (charIdx == -1) { launchWrapperExe = wxString::FromAscii(kLaunchWrapper); } else { launchWrapperExe = runtimeExe.Mid(0, charIdx+1); launchWrapperExe.Append(wxString::FromAscii(kLaunchWrapper)); } printf("Sim launch wrapper: %s\n", (const char*)launchWrapperExe.ToAscii()); argv[arg++] = launchWrapperExeTmp = launchWrapperExe.ToAscii(); launchWrapperArgs.Empty(); pPrefs->GetString("launch-wrapper-args", /*ref*/ launchWrapperArgs); if (!launchWrapperArgs.IsEmpty()) { launchWrapperArgsTmp = launchWrapperArgs.ToAscii(); char* mangle = strdup(launchWrapperArgsTmp); arg = stringToArgv(mangle, argv, arg, kMaxArgs); } /* * If we're launching under GDB or valgrind, set that up. */ doDebug = doValgrind = false; pPrefs->GetBool("debug", &doDebug); if (((MyApp*)wxTheApp)->GetDebuggerOption()) { doDebug = true; } debuggerScript = ((MyApp*)wxTheApp)->GetDebuggerScript(); pPrefs->GetBool("valgrind", &doValgrind); if (doDebug || doValgrind) { pPrefs->GetString("debugger", /*ref*/ debuggerExe); pPrefs->GetString("valgrinder", /*ref*/ valgrinderExe); // check for empty or undefined preferences if (doDebug && debuggerExe.IsEmpty()) { errMsg = wxT("Debugger not defined."); goto bail; } if (doValgrind && valgrinderExe.IsEmpty()) { errMsg = wxT("Valgrinder not defined."); goto bail; } if (doValgrind) { argv[arg++] = valgrinderExeTmp = valgrinderExe.ToAscii(); //argv[arg++] = "--tool=callgrind"; argv[arg++] = "--tool=memcheck"; argv[arg++] = "--leak-check=yes"; // check for leaks too argv[arg++] = "--leak-resolution=med"; // increase from 2 to 4 argv[arg++] = "--num-callers=8"; // reduce from 12 to 8 //argv[arg++] = "--show-reachable=yes"; // show still-reachable if (doDebug) { //mTerminalFollowsChild = true; // interactive argv[arg++] = "--db-attach=yes"; } //mSlowExit = true; } else /*doDebug*/ { argv[arg++] = debuggerExeTmp = debuggerExe.ToAscii(); if (!debuggerScript.IsEmpty()) { argv[arg++] = "-x"; argv[arg++] = debuggerScriptTmp = debuggerScript.ToAscii(); } argv[arg++] = runtimeExeTmp = runtimeExe.ToAscii(); argv[arg++] = "--args"; } } /* * Get runtime args. */ argv[arg++] = runtimeExeTmp = (const char*) runtimeExe.ToAscii(); javaAppName = ((MyApp*)wxTheApp)->GetAutoRunApp(); if (javaAppName.IsEmpty()) { if (!pPrefs->GetString("java-app-name", /*ref*/ javaAppName)) { javaAppName = wxT(""); } } if (!javaAppName.IsEmpty()) { argv[arg++] = "-j"; argv[arg++] = javaAppNameTmp = (const char*) javaAppName.ToAscii(); } if (pPrefs->GetDouble("gamma", &dval) && dval != 1.0) { snprintf(gammaVal, sizeof(gammaVal), "%.3f", dval); argv[arg++] = "-g"; argv[arg++] = gammaVal; } /* finish arg set */ argv[arg++] = NULL; assert(arg <= kMaxArgs); #if 1 printf("ARGS:\n"); for (int i = 0; i < arg; i++) printf(" %d: '%s'\n", i, argv[i]); #endif if (fork() == 0) { execvp(argv[0], (char* const*) argv); fprintf(stderr, "execvp '%s' failed: %s\n", argv[0], strerror(errno)); exit(1); } /* * We assume success; if it didn't succeed we'll just sort of hang * out waiting for a connection. There are ways to fix this (create * a non-close-on-exec pipe and watch to see if the other side closes), * but at this stage it's not worthwhile. */ result = true; tmpStr = wxT("=== launched "); tmpStr += runtimeExe; LogWindow::PostLogMsg(tmpStr); assert(errMsg.IsEmpty()); bail: if (!errMsg.IsEmpty()) { assert(result == false); UserEventMessage* pUem = new UserEventMessage; pUem->CreateErrorMessage(errMsg); UserEvent uev(0, (void*) pUem); assert(statusWindow != NULL); statusWindow->AddPendingEvent(uev); } delete[] argv; return result; } /* * This is the entry point for the device thread. The thread launches the * runtime process and monitors it. When the runtime exits, the thread * exits. * * Because this isn't running in the UI thread, any user interaction has * to be channeled through "user events" to the appropriate window. */ void* DeviceManager::DeviceThread::Entry(void) { //android::MessageStream stream; android::Message msg; wxString errMsg; char statusBuf[64] = "(no status)"; int result = 1; /* print this so we can make sense of log messages */ LOG(LOG_DEBUG, "", "Sim: device management thread starting (pid=%d)\n", getpid()); assert(mReader != NULL && mWriter != NULL); /* * Tell the main thread that we're running. If something fails here, * we'll send them a "stopped running" immediately afterward. */ { UserEventMessage* pUem = new UserEventMessage; pUem->CreateRuntimeStarted(); UserEvent uev(0, (void*) pUem); assert(mpStatusWindow != NULL); mpStatusWindow->AddPendingEvent(uev); } LogWindow::PostLogMsg( "=============================================================="); LogWindow::PostLogMsg("=== runtime starting"); /* * Establish contact with runtime. */ if (!mStream.init(mReader, mWriter, true)) { errMsg = wxT("ERROR: Unable to establish communication with runtime.\n"); goto bail; } /* * Tell the runtime to put itself into a new process group and set * itself up as the foreground process. The latter is only really * necessary to make valgrind+gdb work. */ msg.setCommand(android::Simulator::kCommandNewPGroup, true); mStream.send(&msg); printf("Sim: Sending hardware configuration\n"); /* * Send display config. * * Right now we're just shipping a big binary blob over. */ assert(android::Simulator::kValuesPerDisplay >= 5); int buf[1 + 1 + mpDeviceManager->GetNumDisplays() * android::Simulator::kValuesPerDisplay]; buf[0] = android::Simulator::kDisplayConfigMagic; buf[1] = mpDeviceManager->GetNumDisplays(); for (int i = 0; i < mpDeviceManager->GetNumDisplays(); i++) { DeviceManager::Display* pDisplay = mpDeviceManager->GetDisplay(i); int* pBuf = &buf[2 + android::Simulator::kValuesPerDisplay * i]; pBuf[0] = pDisplay->GetWidth(); pBuf[1] = pDisplay->GetHeight(); pBuf[2] = pDisplay->GetFormat(); pBuf[3] = pDisplay->GetRefresh(); pBuf[4] = pDisplay->GetShmemKey(); } msg.setRaw((const unsigned char*)buf, sizeof(buf), android::Message::kCleanupNoDelete); mStream.send(&msg); /* * Send other hardware config. * * Examples: * - Available input devices. * - Set of buttons on device. * - External devices (Bluetooth, etc). * - Initial mode (e.g. "flipped open" vs. "flipped closed"). */ msg.setConfig("keycharmap", mpDeviceManager->GetKeyMap()); mStream.send(&msg); /* * Done with config. */ msg.setCommand(android::Simulator::kCommandConfigDone, 0); mStream.send(&msg); /* * Sit forever, waiting for messages from the runtime process. */ while (1) { if (!mStream.recv(&msg, true)) { /* * The read failed. This usually means the child has died. */ printf("Sim: runtime process has probably died\n"); break; } if (msg.getType() == android::Message::kTypeCommand) { int cmd, arg; if (!msg.getCommand(&cmd, &arg)) { fprintf(stderr, "Sim: Warning: failed unpacking command\n"); /* keep going? */ } else { switch (cmd) { case android::Simulator::kCommandNewPGroupCreated: // runtime has moved into a separate process group // (not expected for external) printf("Sim: child says it's now in pgrp %d\n", arg); mRuntimeProcessGroup = arg; break; case android::Simulator::kCommandRuntimeReady: // sim is up and running, do late init break; case android::Simulator::kCommandUpdateDisplay: // new frame of graphics is ready //printf("RCVD display update %d\n", arg); mpDeviceManager->ShowFrame(arg); break; case android::Simulator::kCommandVibrate: // vibrator on or off //printf("RCVD vibrator update %d\n", arg); mpDeviceManager->Vibrate(arg); break; default: printf("Sim: got unknown command %d/%d\n", cmd, arg); break; } } } else if (msg.getType() == android::Message::kTypeLogBundle) { android_LogBundle bundle; if (!msg.getLogBundle(&bundle)) { fprintf(stderr, "Sim: Warning: failed unpacking logBundle\n"); /* keep going? */ } else { LogWindow::PostLogMsg(&bundle); } } else { printf("Sim: got unknown message type=%d\n", msg.getType()); } } result = 0; bail: printf("Sim: DeviceManager thread preparing to exit\n"); /* kill the comm channel; should encourage runtime to die */ mStream.close(); delete mReader; delete mWriter; mReader = mWriter = NULL; /* * We never really did get a "friendly death" working, so just slam * the thing if we have the process group. */ if (mRuntimeProcessGroup != 0) { /* kill the group, not our immediate child */ printf("Sim: killing pgrp %d\n", (int) mRuntimeProcessGroup); kill(-mRuntimeProcessGroup, 9); } if (!errMsg.IsEmpty()) { UserEventMessage* pUem = new UserEventMessage; pUem->CreateErrorMessage(errMsg); UserEvent uev(0, (void*) pUem); mpStatusWindow->AddPendingEvent(uev); } /* notify the main window that the runtime has stopped */ { UserEventMessage* pUem = new UserEventMessage; pUem->CreateRuntimeStopped(); UserEvent uev(0, (void*) pUem); mpStatusWindow->AddPendingEvent(uev); } /* show exit status in log file */ wxString exitMsg; exitMsg.Printf(wxT("=== runtime exiting - %s"), statusBuf); LogWindow::PostLogMsg(exitMsg); LogWindow::PostLogMsg( "==============================================================\n"); /* * Reset system properties for future runs. */ ResetProperties(); return (void*) result; } /* * Wait for a little bit to see if the thread will exit. * * "delay" is in 0.1s increments. */ void DeviceManager::DeviceThread::WaitForDeath(int delay) { const int kDelayUnit = 100000; int i; for (i = 0; i < delay; i++) { if (!IsRunning()) return; usleep(kDelayUnit); } } /* * Kill the runtime process. The goal is to cause our local runtime * management thread to exit. If it doesn't, this will kill the thread * before it returns. */ void DeviceManager::DeviceThread::KillChildProcesses(void) { if (!this->IsRunning()) return; /* clear "slow exit" flag -- we're forcefully killing this thing */ //this->mSlowExit = false; /* * Use the ChildProcess object in the thread to send signals. There's * a risk that the DeviceThread will exit and destroy the object while * we're using it. Using a mutex here gets a little awkward because * we can't put it in DeviceThread. It's easier to make a copy of * ChildProcess and operate on the copy, but we have to do that very * carefully to avoid interfering with the communcation pipes. * * For now, we just hope for the best. FIX this someday. * * We broadcast to the process group, which will ordinarily kill * everything. If we're running with valgrind+GDB everything is in our * pgrp and we can't do the broadcast; if GDB alone, then only GDB is * in our pgrp, so the broadcast will hit everything except it. We * hit the group and then hit our child for good measure. */ if (mRuntimeProcessGroup != 0) { /* kill the group, not our immediate child */ printf("Sim: killing pgrp %d\n", (int) mRuntimeProcessGroup); kill(-mRuntimeProcessGroup, 9); WaitForDeath(15); } /* * Close the communication channel. This should cause our thread * to snap out of its blocking read and the runtime thread to bail * out the next time it tries to interact with us. We should only * get here if somebody other than our direct descendant has the * comm channel open and our broadcast didn't work, which should * no longer be possible. */ if (this->IsRunning()) { printf("Sim: killing comm channel\n"); mStream.close(); delete mReader; delete mWriter; mReader = mWriter = NULL; WaitForDeath(15); } /* * At this point it's possible that our DeviceThread is just wedged. * Kill it. * * Using the thread Kill() function can orphan resources, including * locks and semaphores. There is some risk that the simulator will * be hosed after this. */ if (this->IsRunning()) { fprintf(stderr, "Sim: WARNING: killing runtime thread (%ld)\n", (long) GetId()); this->Kill(); WaitForDeath(15); } /* * Now I'm scared. */ if (this->IsRunning()) { fprintf(stderr, "Sim: thread won't die!\n"); } } /* * Configure system properties for the simulated device. * * Property requests can arrive *before* the full connection to the * simulator is established, so we want to reset these during cleanup. */ void DeviceManager::DeviceThread::ResetProperties(void) { wxWindow* mainFrame = ((MyApp*)wxTheApp)->GetMainFrame(); PropertyServer* props = ((MainFrame*)mainFrame)->GetPropertyServer(); props->ClearProperties(); props->SetDefaultProperties(); } #if 0 /* * Return true if the executable found is newer than * what is currently running */ bool DeviceManager::DeviceThread::IsRuntimeNew(void) { if (mLastModified == 0) { /* * Haven't called UpdateLastModified yet, or called it but * couldn't stat() the executable. */ return false; } struct stat status; if (stat(mRuntimeExe.ToAscii(), &status) == 0) { return (status.st_mtime > mLastModified); } else { // doesn't exist, so it can't be newer fprintf(stderr, "Sim: unable to stat '%s': %s\n", (const char*) mRuntimeExe.ToAscii(), strerror(errno)); return false; } } /* * Updates mLastModified to reflect the current executables mtime */ void DeviceManager::DeviceThread::UpdateLastModified(void) { struct stat status; if (stat(mRuntimeExe.ToAscii(), &status) == 0) { mLastModified = status.st_mtime; } else { fprintf(stderr, "Sim: unable to stat '%s': %s\n", (const char*) mRuntimeExe.ToAscii(), strerror(errno)); mLastModified = 0; } } #endif