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
+