auto import from //depot/cupcake/@135843
diff --git a/simulator/app/DeviceManager.cpp b/simulator/app/DeviceManager.cpp
new file mode 100644
index 0000000..a859c63
--- /dev/null
+++ b/simulator/app/DeviceManager.cpp
@@ -0,0 +1,1220 @@
+//
+// 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
+