Initial Contribution
diff --git a/simulator/app/Android.mk b/simulator/app/Android.mk
new file mode 100644
index 0000000..59c147c
--- /dev/null
+++ b/simulator/app/Android.mk
@@ -0,0 +1,130 @@
+# Copyright 2005 The Android Open Source Project
+#
+
+ifneq ($(TARGET_ARCH),arm)
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	DeviceManager.cpp \
+	DeviceWindow.cpp \
+	ExternalRuntime.cpp \
+	LoadableImage.cpp \
+	LocalBiChannel.cpp \
+	LogMessage.cpp \
+	LogPool.cpp \
+	LogPrefsDialog.cpp \
+	LogWindow.cpp \
+	MainFrame.cpp \
+	MessageStream.cpp \
+	MyApp.cpp \
+	PhoneButton.cpp \
+	PhoneCollection.cpp \
+	PhoneData.cpp \
+	PhoneWindow.cpp \
+	Preferences.cpp \
+	PrefsDialog.cpp \
+	PropertyServer.cpp \
+	Semaphore.cpp \
+	Shmem.cpp \
+	UserEvent.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+	libtinyxml
+LOCAL_WHOLE_STATIC_LIBRARIES := \
+	libutils\
+	libcutils
+LOCAL_MODULE := simulator
+
+LOCAL_LDLIBS += -lpthread
+
+LOCAL_CFLAGS := -UNDEBUG
+#LOCAL_LDFLAGS :=
+
+LOCAL_C_INCLUDES += \
+	external/tinyxml \
+	commands/runtime
+
+# wxWidgets defines
+LOCAL_C_INCLUDES += \
+	/usr/include/wx-2.6 \
+	/usr/lib/wx/include/gtk2-unicode-release-2.6
+
+ifeq ($(HOST_OS),linux)
+	# You can install wxWidgets with "sudo apt-get libwxgtk2.6-dev"
+	LOCAL_LDFLAGS += -lwx_baseu-2.6 \
+		-lwx_baseu_net-2.6 \
+		-lwx_baseu_xml-2.6 \
+		-lwx_gtk2u_adv-2.6 \
+		-lwx_gtk2u_core-2.6 \
+		-lwx_gtk2u_html-2.6 \
+		-lwx_gtk2u_qa-2.6 \
+		-lwx_gtk2u_xrc-2.6
+
+	# this next line makes the simulator able to find its shared libraries
+	# without us explicitly setting the LD_LIBRARY_PATH environment variable
+	LOCAL_LDLIBS += -Wl,-z,origin
+	LOCAL_CFLAGS += -DGTK_NO_CHECK_CASTS -D__WXGTK__ -D_FILE_OFFSET_BITS=64 \
+   					-D_LARGE_FILES -D_LARGEFILE_SOURCE=1 
+	LOCAL_LDLIBS += -lrt
+endif
+ifeq ($(HOST_OS),darwin)
+	# NOTE: OS X is no longer supported
+	LOCAL_C_INCLUDES += prebuilt/$(HOST_PREBUILT_TAG)/wxwidgets 
+	LOCAL_LDLIBS += \
+				-framework QuickTime \
+				-framework IOKit \
+				-framework Carbon \
+				-framework Cocoa \
+				-framework System \
+				-lwx_mac_xrc-2.6 \
+				-lwx_mac_qa-2.6 \
+				-lwx_mac_html-2.6 \
+				-lwx_mac_adv-2.6 \
+				-lwx_mac_core-2.6 \
+				-lwx_base_carbon_xml-2.6 \
+				-lwx_base_carbon_net-2.6 \
+				-lwx_base_carbon-2.6 \
+				-lwxexpat-2.6 \
+				-lwxtiff-2.6 \
+				-lwxjpeg-2.6 \
+				-lwxpng-2.6 \
+				-lz \
+				-lpthread \
+				-liconv
+	LOCAL_CFLAGS += \
+				-D__WXMAC__ \
+				-D_FILE_OFFSET_BITS=64 \
+				-D_LARGE_FILES \
+				-DNO_GCC_PRAGMA
+endif
+
+
+include $(BUILD_HOST_EXECUTABLE)
+
+ifeq ($(HOST_OS),darwin)
+# Add the carbon resources to the executable.
+$(LOCAL_BUILT_MODULE): PRIVATE_POST_PROCESS_COMMAND := \
+        /Developer/Tools/Rez -d __DARWIN__ -t APPL \
+        -d __WXMAC__ -o $(LOCAL_BUILT_MODULE) Carbon.r
+endif
+
+# also, we need to copy our assets.  We place these by hand now, because
+# I'd like to clean this up as part of some pdk cleanup I want to do.
+
+asset_files := $(addprefix $(LOCAL_PATH)/assets/,$(call find-subdir-assets,$(LOCAL_PATH)/assets))
+asset_target := $(HOST_COMMON_OUT_ROOT)/sim-assets/simulator$(COMMON_PACKAGE_SUFFIX)
+$(asset_target): PRIVATE_ASSET_ROOT := $(LOCAL_PATH)/assets
+
+$(asset_target) : $(asset_files) $(AAPT)
+	@echo host Package $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(AAPT) package -u -A $(PRIVATE_ASSET_ROOT) -F $@
+
+$(LOCAL_INSTALLED_MODULE): | $(asset_target)
+
+ALL_DEFAULT_INSTALLED_MODULES += $(asset_target)
+
+endif
diff --git a/simulator/app/AssetStream.h b/simulator/app/AssetStream.h
new file mode 100644
index 0000000..0ab2d12
--- /dev/null
+++ b/simulator/app/AssetStream.h
@@ -0,0 +1,102 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Provide a wxInputStream subclass based on the Android Asset class.
+// This is necessary because some wxWidgets functions require either a
+// filename or a wxInputStream (e.g. wxImage).
+//
+#ifndef _SIM_ASSETSTREAM_H
+#define _SIM_ASSETSTREAM_H
+
+#include "wx/stream.h"
+#include <utils/Asset.h>
+
+/*
+ * There is no sample code or concrete documentation about providing
+ * input streams, but it seems straightforward.  The PNG loading code
+ * uses the following:
+ *  OnSysTell()
+ *  OnSysSeek()
+ *  Read()
+ *
+ * The AssetStream takes ownership of the Asset.
+ */
+class AssetStream : public wxInputStream {
+public:
+    AssetStream(android::Asset* pAsset)
+        : mpAsset(pAsset)
+        {}
+    virtual ~AssetStream(void) {
+        delete mpAsset;
+    }
+
+    virtual wxFileOffset GetLength() const {
+        //printf("## GetLength --> %ld\n", (long) mpAsset->getLength());
+        return mpAsset->getLength();
+    }
+    virtual size_t GetSize() const {
+        //printf("## GetSize --> %ld\n", (long) mpAsset->getLength());
+        return mpAsset->getLength();
+    }
+    virtual bool IsSeekable() const { return true; }
+
+    virtual bool Eof() const {
+        //printf("## Eof\n");
+        return (mpAsset->seek(0, SEEK_CUR) == mpAsset->getLength());
+    }
+
+    virtual bool CanRead() const {
+        //printf("## CanRead\n");
+        return !Eof();
+    }
+
+    virtual wxInputStream& Read(void* buffer, size_t size) {
+        OnSysRead(buffer, size);
+
+        return *this;
+    }
+
+protected:
+    /* read data, return number of bytes or 0 if EOF reached */
+    virtual size_t OnSysRead(void* buffer, size_t size) {
+        ssize_t actual = mpAsset->read(buffer, size);
+        if (actual < 0) {
+            // TODO: flag error
+            actual = 0;
+        }
+        //printf("## OnSysRead(%p %u) --> %d\n", buffer, size, actual);
+        return actual;
+    }
+
+    /* seek, using wxWidgets-defined values for "whence" */
+    virtual wxFileOffset OnSysSeek(wxFileOffset seek, wxSeekMode mode) {
+        int whence;
+        off_t newPosn;
+
+        if (mode == wxFromStart)
+            whence = SEEK_SET;
+        else if (mode == wxFromEnd)
+            whence = SEEK_END;
+        else
+            whence = SEEK_CUR;
+        newPosn = mpAsset->seek(seek, whence);
+        //printf("## OnSysSeek(%ld %d) --> %ld\n",
+        //    (long) seek, mode, (long) newPosn);
+        if (newPosn == (off_t) -1)
+            return wxInvalidOffset;
+        else
+            return newPosn;
+    }
+
+    virtual wxFileOffset OnSysTell() const {
+        //printf("## OnSysTell() --> %ld\n", (long) mpAsset->seek(0, SEEK_CUR));
+        return mpAsset->seek(0, SEEK_CUR);
+    }
+
+private:
+    android::Asset*     mpAsset;
+
+    DECLARE_NO_COPY_CLASS(AssetStream);     // private copy-ctor and op=
+};
+
+#endif // _SIM_ASSETSTREAM_H
diff --git a/simulator/app/DeviceManager.cpp b/simulator/app/DeviceManager.cpp
new file mode 100644
index 0000000..1f65d99
--- /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=%d)\n", (int) 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=%d)\n", (int) 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);
+    long 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);
+        long* 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
+
diff --git a/simulator/app/DeviceManager.h b/simulator/app/DeviceManager.h
new file mode 100644
index 0000000..bd4371e
--- /dev/null
+++ b/simulator/app/DeviceManager.h
@@ -0,0 +1,284 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Class that manages the simulated device.
+//
+#ifndef _SIM_DEVICE_MANAGER_H
+#define _SIM_DEVICE_MANAGER_H
+
+#include "UserEvent.h"
+
+#include "Shmem.h"
+#include "MessageStream.h"
+#include "SimRuntime.h"
+
+#include "ui/PixelFormat.h"
+#include "ui/KeycodeLabels.h"
+
+#include <sys/stat.h>
+
+/*
+ * Manage the simulated device.  This includes starting/stopping as well
+ * as sending messages to it and receiving events from it.
+ *
+ * The object may span multiple invocations of a specific device.  If
+ * the simulator is reconfigured to use a device with different
+ * characteristics, the object should be destroyed and recreated (which
+ * guarantees that the runtime is restarted).
+ */
+class DeviceManager {
+public:
+    DeviceManager(void);
+    virtual ~DeviceManager(void);
+
+    /*
+     * Initialize the object.  Call this once.
+     *
+     * "numDisplays" is the number of displays that the simulated hardware
+     * supports.  The displays themselves are configured with separate calls.
+     *
+     * "statusWindow" should be the main frame.  Messages indicating runtime
+     * startup/shutdown are sent, as well as error messages that should be
+     * displayed in message boxes.
+     */
+    bool Init(int numDisplays, wxWindow* statusWindow);
+    bool IsInitialized(void) const;
+
+    /*
+     * Tell the device manager that the windows used to display its output
+     * are closing down.
+     */
+    void WindowsClosing(void);
+
+    /*
+     * "displayWindow" is the window to notify when a new frame of graphics
+     * data is available.  This can be set independently for each display.
+     */
+    bool SetDisplayConfig(int displayIndex, wxWindow* window,
+        int width, int height, android::PixelFormat format, int refresh);
+
+    /*
+     * set the key map
+     */
+    bool SetKeyboardConfig(const char *keymap);
+
+    /*
+     * Return the number of displays we're configured for.
+     */
+    int GetNumDisplays(void) const { return mNumDisplays; }
+
+    /*
+     * Return the shmem key for the Nth display.
+     */
+    //int GetShmemKey(int displayIndex);
+
+    /*
+     * Is the runtime process still running?
+     */
+    bool IsRunning(void) const {
+        if (mThread != NULL)
+            return mThread->IsRunning();
+        return false;
+    }
+    bool IsKillable(void) const {
+        return true;
+    }
+
+    // (Re-)configure the device, e.g. when #of displays changes because
+    // a different phone model has been selected.  Call this before doing
+    // any display-specific setup.  DO NOT call this if the runtime is active.
+//    void Configure(int numDisplays);
+
+    // start the runtime, acting as parent
+    bool StartRuntime(void);
+    // start the runtime, acting as peer
+    bool StartRuntime(android::Pipe* reader, android::Pipe* writer);
+    // politely ask the runtime to stop
+    bool StopRuntime(void);
+    // kill the runtime with extreme prejudice
+    void KillRuntime(void);
+
+#if 0
+    // Returns if the executable is new
+    bool RefreshRuntime(void);
+    // Update the time of the current runtime because the user cancelled a
+    // refresh
+    void UserCancelledRefresh(void);
+#endif
+
+    // send a key-up or key-down event to the runtime
+    void SendKeyEvent(KeyCode keyCode, bool down);
+    // send touch-screen events
+    void SendTouchEvent(android::Simulator::TouchMode mode, int x, int y);
+
+    wxBitmap* GetImageData(int displayIndex);
+    
+    void BroadcastEvent(UserEvent &userEvent);
+
+private:
+    /*
+     * Threads in wxWidgets use sub-classing to define interfaces and
+     * entry points.  We use this to create the thread that interacts
+     * with the runtime.
+     *
+     * The "reader" and "writer" arguments may be NULL.  If they are,
+     * we will launch the runtime ourselves.  If not, we will use them
+     * to speak with an externally-launched runtime process.  The thread
+     * will own the pipes, shutting them down when it exits.
+     */
+    class DeviceThread : public wxThread {
+    public:
+        DeviceThread(DeviceManager* pDM, wxWindow* pStatusWindow,
+            android::Pipe* reader, android::Pipe* writer)
+            : wxThread(wxTHREAD_JOINABLE), mpStatusWindow(pStatusWindow),
+              mReader(reader), mWriter(writer),
+              mpDeviceManager(pDM), /*mTerminalFollowsChild(false),
+              mSlowExit(false), mIsExternal(false), mLastModified(0),*/
+              mRuntimeProcessGroup(0)
+            {}
+        virtual ~DeviceThread(void) {
+            delete mReader;
+            delete mWriter;
+        }
+
+        /* thread entry point */
+        virtual void* Entry(void);
+
+        // wxThread class supplies an IsRunning() method
+
+        /*
+         * This kills the runtime process to force this thread to exit.
+         * If the thread doesn't exit after a short period of time, it
+         * is forcibly terminated.
+         */
+        void KillChildProcesses(void);
+
+#if 0
+        /*
+         * Return if the runtime executable is new
+         */
+        bool IsRuntimeNew(void);
+
+        void UpdateLastModified(void);
+#endif
+
+        android::MessageStream* GetStream(void) { return &mStream; }
+
+        static bool LaunchProcess(wxWindow* statusWindow);
+
+    private:
+        void WaitForDeath(int delay);
+        void ResetProperties(void);
+
+        android::MessageStream  mStream;
+        wxWindow*       mpStatusWindow;
+        android::Pipe*  mReader;
+        android::Pipe*  mWriter;
+        DeviceManager*  mpDeviceManager;
+        pid_t           mRuntimeProcessGroup;
+        //time_t          mLastModified;
+        wxString        mRuntimeExe;
+    };
+
+    friend class DeviceThread;
+
+    /*
+     * We need one of these for each display on the device.  Most devices
+     * only have one, but some flip phones have two.
+     */
+    class Display {
+    public:
+        Display(void)
+            : mDisplayWindow(NULL), mpShmem(NULL), mShmemKey(0),
+              mImageData(NULL), mDisplayNum(-1), mWidth(-1), mHeight(-1),
+              mFormat(android::PIXEL_FORMAT_UNKNOWN), mRefresh(0)
+            {}
+        ~Display() {
+            delete mpShmem;
+            delete[] mImageData;
+        }
+
+        /* initialize goodies */
+        bool Create(int displayNum, wxWindow* window, int width, int height,
+            android::PixelFormat format, int refresh);
+
+        /* call this if we're shutting down soon */
+        void Uncreate(void);
+
+        /* copy & convert data from shared memory */
+        void CopyFromShared(void);
+
+        /* get image data in the form of a 24bpp bitmap */
+        wxBitmap* GetImageData(void);
+
+        /* get a pointer to our display window */
+        wxWindow* GetWindow(void) const { return mDisplayWindow; }
+
+        /* get our shared memory key */
+        int GetShmemKey(void) const { return mShmemKey; }
+
+        int GetWidth(void) const { return mWidth; }
+        int GetHeight(void) const { return mHeight; }
+        android::PixelFormat GetFormat(void) const { return mFormat; }
+        int GetRefresh(void) const { return mRefresh; }
+
+    private:
+        int GenerateKey(int displayNum) {
+            return 0x41544d00 | displayNum;
+        }
+
+        // control access to image data shared between runtime mgr and UI
+        wxMutex         mImageDataLock;
+        // we send an event here when we get stuff to display
+        wxWindow*       mDisplayWindow;
+
+        // shared memory segment
+        android::Shmem* mpShmem;
+        int             mShmemKey;
+
+        // local copy of data from shared mem, converted to 24bpp
+        unsigned char*  mImageData;
+
+        // mainly for debugging -- which display are we?
+        int             mDisplayNum;
+
+        // display characteristics
+        int             mWidth;
+        int             mHeight;
+        android::PixelFormat mFormat;
+        int             mRefresh;       // fps
+    };
+
+    Display* GetDisplay(int dispNum) { return &mDisplay[dispNum]; }
+
+    const char* GetKeyMap() { return mKeyMap ? mKeyMap : "qwerty"; }
+
+    void ShowFrame(int displayIndex);
+
+    void Vibrate(int vibrateOn);
+
+    // get the message stream from the device thread
+    android::MessageStream* GetStream(void);
+
+    // send a request to set the visible layers
+    void SendSetVisibleLayers(void);
+
+    // points at the runtime's thread (while it's running)
+    DeviceThread*   mThread;
+
+    // array of Displays, one per display on the device
+    Display*        mDisplay;
+    int             mNumDisplays;
+
+    // the key map
+    const char * mKeyMap;
+
+    // which graphics layers are visible?
+    int             mVisibleLayers;
+
+    // where to send status messages
+    wxWindow*       mpStatusWindow;
+
+};
+
+#endif // _SIM_DEVICE_MANAGER_H
diff --git a/simulator/app/DeviceWindow.cpp b/simulator/app/DeviceWindow.cpp
new file mode 100644
index 0000000..619a614
--- /dev/null
+++ b/simulator/app/DeviceWindow.cpp
@@ -0,0 +1,264 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Displays output from the 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"   // needed for Windows build
+#include "wx/dcbuffer.h"
+
+#include "AssetStream.h"
+#include "DeviceWindow.h"
+#include "MyApp.h"
+#include "Preferences.h"
+
+BEGIN_EVENT_TABLE(DeviceWindow, wxWindow)
+    EVT_SIZE(DeviceWindow::OnSize)
+    EVT_ERASE_BACKGROUND(DeviceWindow::OnErase)
+    EVT_PAINT(DeviceWindow::OnPaint)
+    EVT_KEY_DOWN(DeviceWindow::OnKeyDown)
+    EVT_KEY_UP(DeviceWindow::OnKeyUp)
+
+    EVT_LEFT_DOWN(DeviceWindow::OnMouseLeftDown)
+    EVT_LEFT_DCLICK(DeviceWindow::OnMouseLeftDown)
+    EVT_LEFT_UP(DeviceWindow::OnMouseLeftUp)
+    EVT_RIGHT_DOWN(DeviceWindow::OnMouseRightDown)
+    EVT_RIGHT_DCLICK(DeviceWindow::OnMouseRightDown)
+    EVT_RIGHT_UP(DeviceWindow::OnMouseRightUp)
+    EVT_MOTION(DeviceWindow::OnMouseMotion)
+
+    EVT_USER_EVENT(DeviceWindow::OnUserEvent)
+END_EVENT_TABLE()
+
+
+/*
+ * Create a new DeviceWindow.  This should be a child of PhoneWindow.
+ *
+ * Note the DeviceManager may not be fully initialized yet.
+ */
+DeviceWindow::DeviceWindow(wxWindow* parent, DeviceManager* pDM)
+    : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
+        wxNO_BORDER | wxWANTS_CHARS),
+      mpDeviceManager(pDM)
+{
+    //printf("DW: created (parent=%p DM=%p)\n", parent, pDM);
+
+    SetBackgroundStyle(wxBG_STYLE_CUSTOM);
+
+    // create a trivial bitmap so we have something allocated
+    mBitmap.Create(1, 1);
+
+}
+
+/*
+ * Destructor.
+ */
+DeviceWindow::~DeviceWindow(void)
+{
+}
+
+/*
+ * We don't want to trap key or mouse events here.
+ *
+ * event.Skip() didn't seem to do the trick, so we call AddPendingEvent()
+ * to add it to the parent's input queue.
+ */
+void DeviceWindow::OnKeyDown(wxKeyEvent& event)
+{
+    //printf("DW: down: %d\n", event.GetKeyCode());
+    GetParent()->AddPendingEvent(event);
+}
+void DeviceWindow::OnKeyUp(wxKeyEvent& event)
+{
+    //printf("DW:   up: %d\n", event.GetKeyCode());
+    GetParent()->AddPendingEvent(event);
+}
+
+/*
+ * Handle mouse events.  We want to pass these up to the PhoneWindow, since
+ * that's where the "touch screen" code is.
+ */
+void DeviceWindow::OnMouseLeftDown(wxMouseEvent& event)
+{
+    ClampMouse(&event);
+    GetParent()->AddPendingEvent(event);
+}
+void DeviceWindow::OnMouseLeftUp(wxMouseEvent& event)
+{
+    ClampMouse(&event);
+    GetParent()->AddPendingEvent(event);
+}
+void DeviceWindow::OnMouseRightDown(wxMouseEvent& event)
+{
+    ClampMouse(&event);
+    GetParent()->AddPendingEvent(event);
+}
+void DeviceWindow::OnMouseRightUp(wxMouseEvent& event)
+{
+    ClampMouse(&event);
+    GetParent()->AddPendingEvent(event);
+}
+void DeviceWindow::OnMouseMotion(wxMouseEvent& event)
+{
+    ClampMouse(&event);
+    GetParent()->AddPendingEvent(event);
+}
+
+/*
+ * Clamp the mouse movement to the window bounds.
+ */
+void DeviceWindow::ClampMouse(wxMouseEvent* pEvent)
+{
+    wxWindow* pEventWindow = (wxWindow*) pEvent->GetEventObject();
+    int width, height;
+
+    pEventWindow->GetSize(&width, &height);
+    if (pEvent->m_x < 0)
+        pEvent->m_x = 0;
+    else if (pEvent->m_x >= width)
+        pEvent->m_x = width-1;
+
+    if (pEvent->m_y < 0)
+        pEvent->m_y = 0;
+    else if (pEvent->m_y >= height)
+        pEvent->m_y = height-1;
+}
+
+
+/*
+ * Handle a "user event".  We get these when the runtime wants us to
+ * know that it has a new frame of graphics to display.
+ * 
+ */
+void DeviceWindow::OnUserEvent(UserEvent& event)
+{
+    wxBitmap* pBitmap;
+    int displayIndex;
+
+    displayIndex = (int) event.GetData();
+
+    //printf("GOT UAE %d\n", displayIndex);
+
+    // a displayIndex of -1 means just update the onion skin
+    if (displayIndex >= 0) {
+        /* get a newly-allocated bitmap with converted image data */
+        pBitmap = mpDeviceManager->GetImageData(displayIndex);
+    
+        /* do a ptr/refcount assignment to hold the data */
+        mBitmap = *pBitmap;
+        /* delete the temporary object; does not delete the bitmap storage */
+        delete pBitmap;
+    }
+    
+    if (displayIndex >= -1) {
+        mHasOnionSkinBitmap = false;
+        
+        Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+        assert(pPrefs != NULL);
+    
+        bool overlayOnionSkin;
+        char* onionSkinFileName = NULL;
+        
+        bool overlayOnionSkinExists = pPrefs->GetBool("overlay-onion-skin", &overlayOnionSkin);
+        if (overlayOnionSkinExists && overlayOnionSkin) {
+            bool fileNameExists = pPrefs->GetString("onion-skin-file-name", &onionSkinFileName);
+            if (fileNameExists && *onionSkinFileName) {
+                wxImage onionSkinImage(wxString::FromAscii(onionSkinFileName));
+                onionSkinImage.SetAlpha(NULL);
+                bool hasAlpha = onionSkinImage.HasAlpha();
+                int width = onionSkinImage.GetWidth();
+                int height = onionSkinImage.GetHeight();
+                if (hasAlpha) {
+                    unsigned char *alpha = onionSkinImage.GetAlpha();
+                    int alphaVal = 127;
+                    pPrefs->GetInt("onion-skin-alpha-value", &alphaVal);
+                    for (int i = (width * height) - 1; i >= 0; i--) {
+                        alpha[i] = alphaVal;
+                    } 
+                }
+                mOnionSkinBitmap = wxBitmap(onionSkinImage);
+                mHasOnionSkinBitmap = true;
+            }
+        }
+    }
+
+    /* induce an update */
+    Refresh();
+}
+
+/*
+ * Window has been moved or resized.
+ *
+ * We get this when the model of phone is changed.
+ *
+ * FIX: in the future this only happens when the phone is rotated 90deg.
+ */
+void DeviceWindow::OnSize(wxSizeEvent& WXUNUSED(event))
+{
+    int width, height;
+
+    GetClientSize(&width, &height);
+    printf("Sim: device window resize: %dx%d\n", width, height);
+
+    mBitmap.Create(width, height);
+
+    wxMemoryDC memDC;
+    memDC.SelectObject(mBitmap);
+
+    wxColour backColor(96, 122, 121);
+    memDC.SetBrush(wxBrush(backColor));
+    memDC.SetPen(wxPen(backColor, 1));
+    wxRect windowRect(wxPoint(0, 0), GetClientSize());
+    memDC.DrawRectangle(windowRect);
+}
+
+/*
+ * No need to erase the background.
+ */
+void DeviceWindow::OnErase(wxEraseEvent& WXUNUSED(event))
+{
+    //printf("erase device\n");
+}
+
+/*
+ * Repaint the simulator output.
+ */
+void DeviceWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
+{
+    wxPaintDC dc(this);
+
+    /* draw background image */
+    dc.DrawBitmap(mBitmap, 0, 0, TRUE);
+
+    /* If necessary, draw onion skin image on top */
+    if (mHasOnionSkinBitmap) {
+        dc.DrawBitmap(mOnionSkinBitmap, 0, 0, TRUE);
+    }
+    
+#if 0
+    // debug - draw the corners
+    int xoff = 0;
+    int yoff = 0;
+    int width;
+    int height;
+    GetClientSize(&width, &height);
+
+    dc.SetPen(*wxGREEN_PEN);
+    dc.DrawLine(xoff,           yoff+9,         xoff,           yoff);
+    dc.DrawLine(xoff,           yoff,           xoff+10,        yoff);
+    dc.DrawLine(xoff+width-10,  yoff,           xoff+width,     yoff);
+    dc.DrawLine(xoff+width-1,   yoff,           xoff+width-1,   yoff+10);
+    dc.DrawLine(xoff,           yoff+height-10, xoff,           yoff+height);
+    dc.DrawLine(xoff,           yoff+height-1,  xoff+10,        yoff+height-1);
+    dc.DrawLine(xoff+width-1,   yoff+height-10, xoff+width-1,   yoff+height);
+    dc.DrawLine(xoff+width-1,   yoff+height-1,  xoff+width-11,  yoff+height-1);
+#endif
+}
+
diff --git a/simulator/app/DeviceWindow.h b/simulator/app/DeviceWindow.h
new file mode 100644
index 0000000..4548fc3
--- /dev/null
+++ b/simulator/app/DeviceWindow.h
@@ -0,0 +1,70 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Window with simulated phone.
+//
+#ifndef _SIM_DEVICE_WINDOW_H
+#define _SIM_DEVICE_WINDOW_H
+
+#include "UserEvent.h"
+#include "DeviceManager.h"
+
+/*
+ * This window displays the device output.
+ */
+class DeviceWindow : public wxWindow {
+public:
+    DeviceWindow(wxWindow* parent, DeviceManager* pDM);
+    virtual ~DeviceWindow(void);
+
+#if 0       // can't work -- can't create bitmaps in other threads
+    /* this gets tucked into a user event */
+    class FrameData {
+    public:
+        FrameData(void)
+            : mDisplayIndex(-1), mpBitmap(NULL)
+            {}
+        ~FrameData(void) {
+            delete mpBitmap;
+        }
+
+        void Create(int displayIndex, wxBitmap* pBitmap) {
+            mDisplayIndex = displayIndex;
+            mpBitmap = pBitmap;
+        }
+
+        int GetDisplayIndex(void) const { return mDisplayIndex; }
+        wxBitmap* GetBitmap(void) const { return mpBitmap; }
+
+    private:
+        int         mDisplayIndex;
+        wxBitmap*   mpBitmap;
+    };
+#endif
+
+    void DeviceManagerClosing(void) { mpDeviceManager = NULL; }
+
+private:
+    void OnKeyDown(wxKeyEvent& event);
+    void OnKeyUp(wxKeyEvent& event);
+    void OnMouseLeftDown(wxMouseEvent& event);
+    void OnMouseLeftUp(wxMouseEvent& event);
+    void OnMouseRightDown(wxMouseEvent& event);
+    void OnMouseRightUp(wxMouseEvent& event);
+    void OnMouseMotion(wxMouseEvent& event);
+    void OnSize(wxSizeEvent& WXUNUSED(event));
+    void OnErase(wxEraseEvent& event);
+    void OnPaint(wxPaintEvent& WXUNUSED(event));
+    void OnUserEvent(UserEvent& event);
+
+    void ClampMouse(wxMouseEvent* pEvent);
+
+    DeviceManager*  mpDeviceManager;
+    wxBitmap    mBitmap;
+    wxBitmap	mOnionSkinBitmap;
+    bool        mHasOnionSkinBitmap;
+
+    DECLARE_EVENT_TABLE()
+};
+
+#endif // _SIM_DEVICE_WINDOW_H
diff --git a/simulator/app/ExternalRuntime.cpp b/simulator/app/ExternalRuntime.cpp
new file mode 100644
index 0000000..9f2cc5e
--- /dev/null
+++ b/simulator/app/ExternalRuntime.cpp
@@ -0,0 +1,94 @@
+//
+// 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 "ExternalRuntime.h"
+#include "MyApp.h"
+#include "UserEvent.h"
+#include "UserEventMessage.h"
+
+#include "SimRuntime.h"
+#include "LocalBiChannel.h"
+#include "utils.h"
+
+
+using namespace android;
+
+/*
+ * Destructor.
+ */
+ExternalRuntime::~ExternalRuntime(void)
+{
+    if (IsRunning()) {
+        // TODO: cause thread to stop, then Wait for it
+    }
+    printf("Sim: in ~ExternalRuntime()\n");
+}
+
+/*
+ * Create and run the thread.
+ */
+bool ExternalRuntime::StartThread(void)
+{
+    if (Create() != wxTHREAD_NO_ERROR) {
+        fprintf(stderr, "Sim: ERROR: can't create ExternalRuntime thread\n");
+        return false;
+    }
+
+    Run();
+    return true;
+}
+
+/*
+ * Thread entry point.
+ *
+ * This just sits and waits for a new connection.  It hands it off to the
+ * main thread and then goes back to waiting.
+ *
+ * There is currently no "polite" way to shut this down.
+ */
+void* ExternalRuntime::Entry(void)
+{
+    LocalBiChannel lbic;
+    Pipe* reader;
+    Pipe* writer;
+
+    reader = writer = NULL;
+
+    if (!lbic.create(ANDROID_PIPE_NAME)) {
+        fprintf(stderr, "Sim: failed creating named pipe '%s'\n",
+            ANDROID_PIPE_NAME);
+        return NULL;
+    }
+
+    while (lbic.listen(&reader, &writer)) {
+        /*
+         * Throw it over the wall.
+         */
+        wxWindow* pMainFrame = ((MyApp*)wxTheApp)->GetMainFrame();
+
+        UserEventMessage* pUem = new UserEventMessage;
+        pUem->CreateExternalRuntime(reader, writer);
+
+        UserEvent uev(0, (void*) pUem);
+        pMainFrame->AddPendingEvent(uev);
+
+        reader = writer = NULL;
+    }
+
+    printf("Sim: ExternalRuntime thread wants to bail\n");
+
+    return NULL;
+}
+
diff --git a/simulator/app/ExternalRuntime.h b/simulator/app/ExternalRuntime.h
new file mode 100644
index 0000000..774a2cd
--- /dev/null
+++ b/simulator/app/ExternalRuntime.h
@@ -0,0 +1,26 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Class that manages the simulated device.
+//
+#ifndef _SIM_EXTERNAL_RUNTIME_H
+#define _SIM_EXTERNAL_RUNTIME_H
+
+/*
+ * Define a thread that listens for the launch of an external runtime.
+ * When we spot one we notify the main thread, which can choose to
+ * accept or reject it.
+ */
+class ExternalRuntime : public wxThread {
+public:
+    ExternalRuntime(void) {}
+    virtual ~ExternalRuntime(void);
+
+    /* start the thread running */
+    bool StartThread(void);
+
+    /* thread entry point */
+    virtual void* Entry(void);
+};
+
+#endif // _SIM_EXTERNAL_RUNTIME_H
diff --git a/simulator/app/LinuxKeys.h b/simulator/app/LinuxKeys.h
new file mode 100644
index 0000000..6382de9
--- /dev/null
+++ b/simulator/app/LinuxKeys.h
@@ -0,0 +1,20 @@
+#ifndef _SIM_LINUXKEYS_H
+#define _SIM_LINUXKEYS_H
+
+#include <linux/input.h>
+
+/* ubuntu has these, goobuntu doesn't */
+#ifndef KEY_SWITCHVIDEOMODE
+# define KEY_SWITCHVIDEOMODE 227
+#endif
+#ifndef KEY_KBDILLUMTOGGLE
+# define KEY_KBDILLUMTOGGLE 228
+#endif
+#ifndef KEY_KBDILLUMUP
+# define KEY_KBDILLUMUP     230
+#endif
+#ifndef KEY_REPLY
+# define KEY_REPLY          232
+#endif
+
+#endif /*_SIM_LINUXKEYS_H*/
diff --git a/simulator/app/LoadableImage.cpp b/simulator/app/LoadableImage.cpp
new file mode 100644
index 0000000..e5bd0f3
--- /dev/null
+++ b/simulator/app/LoadableImage.cpp
@@ -0,0 +1,100 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Simple class to hold an image that can be loaded and unloaded.
+//
+
+// 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"   // needed for Windows build
+
+#include "LoadableImage.h"
+#include "AssetStream.h"
+#include "MyApp.h"
+
+#include <utils.h>
+
+#include <stdio.h>
+
+
+/*
+ * Load the image.
+ */
+bool LoadableImage::Create(const char* fileName, int x, int y)
+{
+    if (fileName == NULL || x < 0 || y < 0) {
+        fprintf(stderr, "bad params to %s\n", __PRETTY_FUNCTION__);
+        return false;
+    }
+
+    delete[] mName;
+    mName = android::strdupNew(fileName);
+
+    mX = x;
+    mY = y;
+
+    return true;
+}
+
+/*
+ * Load the bitmap.
+ */
+bool LoadableImage::LoadResources(void)
+{
+    if (mName == NULL)
+        return false;
+
+    if (mpBitmap != NULL)       // already loaded?
+        return true;
+
+    //printf("LoadResources: '%s'\n", (const char*) mName);
+#ifdef BEFORE_ASSET
+    wxImage img(mName);
+#else
+    android::AssetManager* pAssetMgr = ((MyApp*)wxTheApp)->GetAssetManager();
+    android::Asset* pAsset;
+
+    pAsset = pAssetMgr->open(mName, android::Asset::ACCESS_RANDOM);
+    if (pAsset == NULL) {
+        fprintf(stderr, "ERROR: unable to load '%s'\n", mName);
+        return false;
+    } else {
+        //printf("--- opened asset '%s'\n",
+        //    (const char*) pAsset->getAssetSource());
+    }
+    AssetStream astr(pAsset);
+
+    wxImage img(astr);
+#endif
+
+    mWidth = img.GetWidth();
+    mHeight = img.GetHeight();
+    if (mWidth <= 0 || mHeight <= 0) {
+        /* image failed to load or decode */
+        fprintf(stderr, "ERROR: unable to load/decode '%s'\n", mName);
+        //delete img;
+        return false;
+    }
+
+    mpBitmap = new wxBitmap(img);
+
+    //delete img;
+
+    return true;
+}
+
+/*
+ * Unload the bitmap.
+ */
+bool LoadableImage::UnloadResources(void)
+{
+    delete mpBitmap;
+    mpBitmap = NULL;
+    return true;
+}
+
diff --git a/simulator/app/LoadableImage.h b/simulator/app/LoadableImage.h
new file mode 100644
index 0000000..368d520
--- /dev/null
+++ b/simulator/app/LoadableImage.h
@@ -0,0 +1,73 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Simulated device definition.
+//
+#ifndef _SIM_LOADABLE_IMAGE_H
+#define _SIM_LOADABLE_IMAGE_H
+
+#include "utils.h"
+
+/*
+ * Holds an image that may or may not be loaded at present.  The image
+ * has an (x,y) offset.
+ */
+class LoadableImage {
+public:
+    LoadableImage(void)
+        : mName(NULL), mpBitmap(NULL), mX(-1), mY(-1), mWidth(-1), mHeight(-1)
+        {}
+    virtual ~LoadableImage(void) {
+        delete[] mName;
+        delete mpBitmap;
+    }
+    LoadableImage(const LoadableImage& src)
+        : mName(NULL), mpBitmap(NULL)
+    {
+        CopyMembers(src);
+    }
+    LoadableImage& operator=(const LoadableImage& src) {
+        if (this != &src)       // self-assignment
+            CopyMembers(src);
+        return *this;
+    }
+    void CopyMembers(const LoadableImage& src) {
+        // Need to delete resources in case we're using operator= and
+        // assigning into an object that already holds some.
+        delete mName;
+        delete mpBitmap;
+        mName = android::strdupNew(src.mName);
+        if (src.mpBitmap == NULL)
+            mpBitmap = NULL;
+        else
+            mpBitmap = new wxBitmap(*(src.mpBitmap));
+        mX = src.mX;
+        mY = src.mY;
+        mWidth = src.mWidth;
+        mHeight = src.mHeight;
+    }
+
+    virtual bool Create(const char* fileName, int x, int y);
+
+    // load or unload the bitmap
+    bool LoadResources(void);
+    bool UnloadResources(void);
+
+    // accessors
+    int GetX(void) const { return mX; }
+    int GetY(void) const { return mY; }
+    int GetWidth(void) const { return mWidth; }
+    int GetHeight(void) const { return mHeight; }
+    wxBitmap* GetBitmap(void) const { return mpBitmap; }
+
+private:
+    char*       mName;
+    wxBitmap*   mpBitmap;
+
+    int         mX;         // position relative to phone image
+    int         mY;
+    int         mWidth;     // from image (cached values)
+    int         mHeight;
+};
+
+#endif // _SIM_LOADABLE_IMAGE_H
diff --git a/simulator/app/LocalBiChannel.cpp b/simulator/app/LocalBiChannel.cpp
new file mode 100644
index 0000000..93a997d
--- /dev/null
+++ b/simulator/app/LocalBiChannel.cpp
@@ -0,0 +1,444 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Local named bi-directional communication channel.
+//
+#include "LocalBiChannel.h"
+#include "utils/Log.h"
+
+#if defined(HAVE_WIN32_IPC)
+# define _WIN32_WINNT 0x0500
+# include <windows.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <sys/stat.h>
+# include <sys/un.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#ifndef SUN_LEN
+/*
+ * Our current set of ARM header files don't define this.
+ */
+# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path)        \
+                      + strlen ((ptr)->sun_path))
+#endif
+
+using namespace android;
+
+const unsigned long kInvalidHandle = (unsigned long) -1;
+
+/*
+ * Initialize data fields.
+ */
+LocalBiChannel::LocalBiChannel(void)
+    : mFileName(NULL), mIsListener(false), mHandle(kInvalidHandle)
+{
+}
+
+#if defined(HAVE_WIN32_IPC)
+/*
+ * Implementation for Win32, using named pipes.
+ *
+ * Cygwin actually supports UNIX-domain sockets, but we want to stuff
+ * the file handles into a Pipe, which uses HANDLE under Win32.
+ */
+
+const int kPipeSize = 4096;
+
+/*
+ * Destructor.  If we're the server side, we may need to clean up after
+ * ourselves.
+ */
+LocalBiChannel::~LocalBiChannel(void)
+{
+    if (mHandle != kInvalidHandle)
+        CloseHandle((HANDLE)mHandle);
+
+    delete[] mFileName;
+}
+
+/*
+ * Construct the full path.  The caller must delete[] the return value.
+ */
+static char* makeFilename(const char* name)
+{
+    static const char* kBasePath = "\\\\.\\pipe\\android-";
+    char* fileName;
+
+    assert(name != NULL && name[0] != '\0');
+
+    fileName = new char[strlen(kBasePath) + strlen(name) + 1];
+    strcpy(fileName, kBasePath);
+    strcat(fileName, name);
+
+    return fileName;
+}
+
+/*
+ * Create a named pipe, so the client has something to connect to.
+ */
+bool LocalBiChannel::create(const char* name)
+{
+    delete[] mFileName;
+    mFileName = makeFilename(name);
+
+#if 0
+    HANDLE hPipe;
+
+    hPipe = CreateNamedPipe(
+                    mFileName,              // unique pipe name
+                    PIPE_ACCESS_DUPLEX |    // open mode
+                        FILE_FLAG_FIRST_PIPE_INSTANCE,
+                    0,                      // pipe mode (byte, blocking)
+                    1,                      // max instances
+                    kPipeSize,              // output buffer
+                    kPipeSize,              // input buffer
+                    NMPWAIT_USE_DEFAULT_WAIT,   // client time-out
+                    NULL);                  // security
+
+    if (hPipe == 0) {
+        LOG(LOG_ERROR, "lbicomm",
+            "CreateNamedPipe failed (err=%ld)\n", GetLastError());
+        return false;
+    }
+
+    mHandle = (unsigned long) hPipe;
+#endif
+
+    return true;
+}
+
+/*
+ * Attach to an existing named pipe.
+ */
+bool LocalBiChannel::attach(const char* name, Pipe** ppReadPipe,
+    Pipe** ppWritePipe)
+{
+    HANDLE hPipe, dupHandle;
+
+    delete[] mFileName;
+    mFileName = makeFilename(name);
+
+    hPipe = CreateFile(
+                mFileName,                      // filename
+                GENERIC_READ | GENERIC_WRITE,   // access
+                0,                              // no sharing
+                NULL,                           // security
+                OPEN_EXISTING,                  // don't create
+                0,                              // attributes
+                NULL);                          // template
+    if (hPipe == INVALID_HANDLE_VALUE) {
+        LOG(LOG_ERROR, "lbicomm",
+            "CreateFile on pipe '%s' failed (err=%ld)\n", name, GetLastError());
+        return false;
+    }
+
+    assert(mHandle == kInvalidHandle);
+
+    /*
+     * Set up the pipes.  Use the new handle for one, and a duplicate
+     * of it for the other, in case we decide to only close one side.
+     */
+    *ppReadPipe = new Pipe();
+    (*ppReadPipe)->createReader((unsigned long) hPipe);
+
+    DuplicateHandle(
+            GetCurrentProcess(),
+            hPipe,
+            GetCurrentProcess(),
+            &dupHandle,
+            0,
+            FALSE,
+            DUPLICATE_SAME_ACCESS);
+    *ppWritePipe = new Pipe();
+    (*ppWritePipe)->createWriter((unsigned long) dupHandle);
+
+    return true;
+}
+
+/*
+ * Listen for a new connection, discarding any existing connection.
+ */
+bool LocalBiChannel::listen(Pipe** ppReadPipe, Pipe** ppWritePipe)
+{
+    BOOL connected;
+    HANDLE hPipe;
+
+    /*
+     * Create up to 3 instances of the named pipe:
+     * - currently active connection
+     * - connection currently being rejected because one is already active
+     * - a new listener to wait for the next round
+     */
+    hPipe = CreateNamedPipe(
+                    mFileName,              // unique pipe name
+                    PIPE_ACCESS_DUPLEX      // open mode
+                        /*| FILE_FLAG_FIRST_PIPE_INSTANCE*/,
+                    0,                      // pipe mode (byte, blocking)
+                    3,                      // max instances
+                    kPipeSize,              // output buffer
+                    kPipeSize,              // input buffer
+                    NMPWAIT_USE_DEFAULT_WAIT,   // client time-out
+                    NULL);                  // security
+
+    if (hPipe == 0) {
+        LOG(LOG_ERROR, "lbicomm",
+            "CreateNamedPipe failed (err=%ld)\n", GetLastError());
+        return false;
+    }
+
+    /*
+     * If a client is already connected to us, this fails with
+     * ERROR_PIPE_CONNECTED.  It returns success if we had to wait
+     * a little bit before the connection happens.
+     */
+    connected = ConnectNamedPipe(hPipe, NULL) ?
+        TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
+
+    if (connected) {
+        /*
+         * Create the pipes.  Give one a duplicated handle so that,
+         * when one closes, we don't lose both.
+         */
+        HANDLE dupHandle;
+
+        *ppReadPipe = new Pipe();
+        (*ppReadPipe)->createReader((unsigned long) hPipe);
+
+        DuplicateHandle(
+                GetCurrentProcess(),
+                hPipe,
+                GetCurrentProcess(),
+                &dupHandle,
+                0,
+                FALSE,
+                DUPLICATE_SAME_ACCESS);
+        *ppWritePipe = new Pipe();
+        (*ppWritePipe)->createWriter((unsigned long) dupHandle);
+
+        return true;
+    } else {
+        LOG(LOG_WARN, "lbicomm",
+            "ConnectNamedPipe failed (err=%ld)\n", GetLastError());
+#ifdef HAVE_WIN32_THREADS
+        Sleep(500); /* 500 ms */
+#else            
+        usleep(500000);     // DEBUG DEBUG
+#endif        
+        return false;
+    }
+}
+
+#else
+
+/*
+ * Implementation for Linux and Darwin, using UNIX-domain sockets.
+ */
+
+/*
+ * Destructor.  If we're the server side, blow away the socket file.
+ */
+LocalBiChannel::~LocalBiChannel(void)
+{
+    if (mHandle != kInvalidHandle)
+        close((int) mHandle);
+
+    if (mIsListener && mFileName != NULL) {
+        LOG(LOG_DEBUG, "lbicomm", "Removing '%s'\n", mFileName);
+        (void) unlink(mFileName);
+    }
+    delete[] mFileName;
+}
+
+/*
+ * Construct the full path.  The caller must delete[] the return value.
+ */
+static char* makeFilename(const char* name)
+{
+    static const char* kBasePath = "/tmp/android-";
+    char* fileName;
+
+    assert(name != NULL && name[0] != '\0');
+
+    fileName = new char[strlen(kBasePath) + strlen(name) + 1];
+    strcpy(fileName, kBasePath);
+    strcat(fileName, name);
+
+    return fileName;
+}
+
+/*
+ * Create a UNIX domain socket, carefully removing it if it already
+ * exists.
+ */
+bool LocalBiChannel::create(const char* name)
+{
+    struct stat sb;
+    bool result = false;
+    int sock = -1;
+    int cc;
+
+    delete[] mFileName;
+    mFileName = makeFilename(name);
+
+    cc = stat(mFileName, &sb);
+    if (cc < 0) {
+        if (errno != ENOENT) {
+            LOG(LOG_ERROR, "lbicomm",
+                "Unable to stat '%s' (errno=%d)\n", mFileName, errno);
+            goto bail;
+        }
+    } else {
+        /* don't touch it if it's not a socket */
+        if (!(S_ISSOCK(sb.st_mode))) {
+            LOG(LOG_ERROR, "lbicomm",
+                "File '%s' exists and is not a socket\n", mFileName);
+            goto bail;
+        }
+
+        /* remove the cruft */
+        if (unlink(mFileName) < 0) {
+            LOG(LOG_ERROR, "lbicomm",
+                "Unable to remove '%s' (errno=%d)\n", mFileName, errno);
+            goto bail;
+        }
+    }
+
+    struct sockaddr_un addr;
+
+    sock = ::socket(AF_UNIX, SOCK_STREAM, 0);
+    if (sock < 0) {
+        LOG(LOG_ERROR, "lbicomm",
+            "UNIX domain socket create failed (errno=%d)\n", errno);
+        goto bail;
+    }
+
+    /* bind the socket; this creates the file on disk */
+    strcpy(addr.sun_path, mFileName);    // max 108 bytes
+    addr.sun_family = AF_UNIX;
+    cc = ::bind(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
+    if (cc < 0) {
+        LOG(LOG_ERROR, "lbicomm",
+            "AF_UNIX bind failed for '%s' (errno=%d)\n", mFileName, errno);
+        goto bail;
+    }
+
+    mHandle = (unsigned long) sock;
+    sock = -1;
+    mIsListener = true;
+    result = true;
+
+bail:
+    if (sock >= 0)
+        close(sock);
+    return result;
+}
+
+/*
+ * Attach to an existing UNIX domain socket.
+ */
+bool LocalBiChannel::attach(const char* name, Pipe** ppReadPipe,
+    Pipe** ppWritePipe)
+{
+    bool result = false;
+    int sock = -1;
+    int cc;
+
+    assert(ppReadPipe != NULL);
+    assert(ppWritePipe != NULL);
+
+    delete[] mFileName;
+    mFileName = makeFilename(name);
+
+    struct sockaddr_un addr;
+
+    sock = ::socket(AF_UNIX, SOCK_STREAM, 0);
+    if (sock < 0) {
+        LOG(LOG_ERROR, "lbicomm",
+            "UNIX domain socket create failed (errno=%d)\n", errno);
+        goto bail;
+    }
+
+    /* connect to socket; fails if file doesn't exist */
+    strcpy(addr.sun_path, mFileName);    // max 108 bytes
+    addr.sun_family = AF_UNIX;
+    cc = ::connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
+    if (cc < 0) {
+        // ENOENT means socket file doesn't exist
+        // ECONNREFUSED means socket exists but nobody is listening
+        LOG(LOG_ERROR, "lbicomm",
+            "AF_UNIX connect failed for '%s': %s\n", mFileName,strerror(errno));
+        goto bail;
+    }
+
+    /*
+     * Create the two halves.  We dup() the sock so that closing one side
+     * does not hose the other.
+     */
+    *ppReadPipe = new Pipe();
+    (*ppReadPipe)->createReader(sock);
+    *ppWritePipe = new Pipe();
+    (*ppWritePipe)->createWriter(dup(sock));
+
+    assert(mHandle == kInvalidHandle);
+    sock = -1;
+    mIsListener = false;
+
+    result = true;
+
+bail:
+    if (sock >= 0)
+        close(sock);
+    return result;
+}
+
+/*
+ * Listen for a new connection.
+ */
+bool LocalBiChannel::listen(Pipe** ppReadPipe, Pipe** ppWritePipe)
+{
+    bool result = false;
+    struct sockaddr_un from;
+    socklen_t fromlen;
+    int sock, lsock;
+    int cc;
+
+    assert(mHandle != kInvalidHandle);
+    lsock = (int) mHandle;
+
+    LOG(LOG_DEBUG, "lbicomm", "AF_UNIX listening\n");
+    cc = ::listen(lsock, 5);
+    if (cc < 0) {
+        LOG(LOG_ERROR, "lbicomm", "AF_UNIX listen failed (errno=%d)\n", errno);
+        goto bail;
+    }
+
+    fromlen = sizeof(from);     // not SUN_LEN()
+    sock = ::accept(lsock, (struct sockaddr*) &from, &fromlen);
+    if (sock < 0) {
+        LOG(LOG_WARN, "lbicomm", "AF_UNIX accept failed (errno=%d)\n", errno);
+        goto bail;
+    }
+
+    /*
+     * Create the two halves.  We dup() the sock so that closing one side
+     * does not hose the other.
+     */
+    *ppReadPipe = new Pipe();
+    (*ppReadPipe)->createReader(sock);
+    *ppWritePipe = new Pipe();
+    (*ppWritePipe)->createWriter(dup(sock));
+    result = true;
+
+bail:
+    return result;
+}
+
+#endif
diff --git a/simulator/app/LocalBiChannel.h b/simulator/app/LocalBiChannel.h
new file mode 100644
index 0000000..ce04bc0
--- /dev/null
+++ b/simulator/app/LocalBiChannel.h
@@ -0,0 +1,54 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Create or attach to a named bi-directional channel on the local machine.
+//
+#ifndef __LIBS_LOCALBICHANNEL_H
+#define __LIBS_LOCALBICHANNEL_H
+
+#ifdef HAVE_ANDROID_OS
+#error DO NOT USE THIS FILE IN THE DEVICE BUILD
+#endif
+
+#include <utils/Pipe.h>
+
+namespace android {
+
+/*
+ * This is essentially a wrapper class for UNIX-domain sockets.  The
+ * idea is to set one up with create() or attach to one with attach()
+ * and then extract the unidirectional read/write Pipes.  These can
+ * be used directly or stuffed into a MessageStream.
+ *
+ * The name for the pipe should be a short filename made up of alphanumeric
+ * characters.  Depending on the implementation, we may create a file in
+ * /tmp with the specified name, removing any existing copy.
+ */
+class LocalBiChannel {
+public:
+    LocalBiChannel(void);
+    ~LocalBiChannel(void);
+
+    /* create the "listen" side */
+    bool create(const char* name);
+
+    /*
+     * Listen for a connection.  When we get one, we create unidirectional
+     * read/write pipes and return them.
+     */
+    bool listen(Pipe** ppReadPipe, Pipe** ppWritePipe);
+
+    /*
+     * Attach to a channel created by somebody else.  Returns pipes.
+     */
+    bool attach(const char* name, Pipe** ppReadPipe, Pipe** ppWritePipe);
+
+private:
+    char*       mFileName;
+    bool        mIsListener;
+    unsigned long mHandle;
+};
+
+}; // namespace android
+
+#endif // __LIBS_LOCALBICHANNEL_H
diff --git a/simulator/app/LogBundle.h b/simulator/app/LogBundle.h
new file mode 100644
index 0000000..2cba97b
--- /dev/null
+++ b/simulator/app/LogBundle.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _SIM_LOG_BUNDLE_H
+#define _SIM_LOG_BUNDLE_H
+
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+#include <cutils/logd.h> // for android_LogPriority.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct android_LogBundle {
+    time_t              when;
+    android_LogPriority priority;
+    pid_t               pid;
+#ifndef HAVE_PTHREADS
+    unsigned            tid;
+#else    
+    pthread_t           tid;
+#endif    
+    const char*         tag;
+    const struct iovec* msgVec;
+    size_t              msgCount;
+    int                 fd;
+} android_LogBundle;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SIM_LOG_BUNDLE_H
diff --git a/simulator/app/LogMessage.cpp b/simulator/app/LogMessage.cpp
new file mode 100644
index 0000000..63cb764
--- /dev/null
+++ b/simulator/app/LogMessage.cpp
@@ -0,0 +1,98 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Hold a single log message.
+//
+#include "LogMessage.h"
+#include <assert.h>
+
+/*
+ * Constructor.
+ *
+ * Initializers here aren't necessary, since we can only create one of
+ * these through Create(), which touches every field.
+ */
+LogMessage::LogMessage(void)
+{
+}
+
+/*
+ * Destructor.
+ */
+LogMessage::~LogMessage(void)
+{
+    delete[] mTag;
+    delete[] mMsg;
+}
+
+/*
+ * Create a new LogMessage object, and populate it with the contents of
+ * "*pBundle".
+ */
+/*static*/ LogMessage* LogMessage::Create(const android_LogBundle* pBundle)
+{
+    LogMessage* newMsg = new LogMessage;
+
+    if (newMsg == NULL)
+        return NULL;
+    assert(pBundle != NULL);
+
+    newMsg->mWhen = pBundle->when;
+    newMsg->mPriority = pBundle->priority;
+    newMsg->mPid = pBundle->pid;
+    newMsg->mTag = android::strdupNew(pBundle->tag);
+
+    size_t len = 0;
+    size_t i;
+    for (i=0; i<pBundle->msgCount; i++) len += pBundle->msgVec[i].iov_len;
+    newMsg->mMsg = new char[len+1];
+    char* p = newMsg->mMsg;
+    for (i=0; i<pBundle->msgCount; i++) {
+        memcpy(p, pBundle->msgVec[i].iov_base, pBundle->msgVec[i].iov_len);
+        p += pBundle->msgVec[i].iov_len;
+    }
+    *p = 0;
+
+    newMsg->mRefCnt = 1;
+    newMsg->mInternal = false;
+    newMsg->mFootprint = 8 * sizeof(int) + strlen(newMsg->mTag) +
+        strlen(newMsg->mMsg) + 4;
+    newMsg->mTextCtrlLen = 0;
+    newMsg->mpPrev = NULL;
+    newMsg->mpNext = NULL;
+
+    return newMsg;
+}
+
+/*
+ * Create a new LogMessage object, with a simple message in it.
+ *
+ * Sets "mInternal" so we display it appropriately.
+ */
+/*static*/ LogMessage* LogMessage::Create(const char* msg)
+{
+    LogMessage* newMsg;
+    android_LogBundle bundle;
+
+    assert(msg != NULL);
+
+    memset(&bundle, 0, sizeof(bundle));
+    bundle.when = time(NULL);
+    bundle.priority = ANDROID_LOG_ERROR;
+    bundle.pid = getpid();
+    bundle.tag = "-";
+    iovec iov;
+    iov.iov_base = (void*)msg;
+    iov.iov_len = strlen(msg);
+    bundle.msgVec = &iov;
+    bundle.msgCount = 1;
+
+    newMsg = Create(&bundle);
+
+    if (newMsg != NULL) {
+        newMsg->mInternal = true;
+    }
+
+    return newMsg;
+}
+
diff --git a/simulator/app/LogMessage.h b/simulator/app/LogMessage.h
new file mode 100644
index 0000000..fe77dc0
--- /dev/null
+++ b/simulator/app/LogMessage.h
@@ -0,0 +1,73 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Class to hold a single log message.  Not thread safe.
+//
+#ifndef _SIM_LOG_MESSAGE_H
+#define _SIM_LOG_MESSAGE_H
+
+#include "utils.h"
+#include "LogBundle.h"
+
+/*
+ * Hold a single log message.
+ *
+ * To reduce malloc strain we could over-allocate the object and tuck the
+ * message text into the object storage.  On this off chance this becomes
+ * important, the implementation keeps its constructor private.
+ */
+class LogMessage {
+public:
+    ~LogMessage(void);
+
+    static LogMessage* Create(const android_LogBundle* pBundle);
+    static LogMessage* Create(const char* msg);
+
+    /* the total length of text added to the text ctrl */
+    int GetTextCtrlLen(void) const { return mTextCtrlLen; }
+    void SetTextCtrlLen(int len) { mTextCtrlLen = len; }
+
+    /* log pool */
+    LogMessage* GetPrev(void) const { return mpPrev; }
+    void SetPrev(LogMessage* pPrev) { mpPrev = pPrev; }
+    LogMessage* GetNext(void) const { return mpNext; }
+    void SetNext(LogMessage* pNext) { mpNext = pNext; }
+    int GetFootprint(void) const { return mFootprint; }
+
+    /* message contents */
+    time_t GetWhen(void) const { return mWhen; }
+    android_LogPriority GetPriority(void) const { return mPriority; }
+    pid_t GetPid(void) const { return mPid; }
+    const char* GetTag(void) const { return mTag; }
+    const char* GetMsg(void) const { return mMsg; }
+
+    bool GetInternal(void) const { return mInternal; }
+
+    void Acquire(void) { mRefCnt++; }
+    void Release(void) {
+        if (!--mRefCnt)
+            delete this;
+    }
+
+private:
+    LogMessage(void);
+    LogMessage(const LogMessage& src);              // not implemented
+    LogMessage& operator=(const LogMessage& src);   // not implemented
+
+    /* log message contents */
+    time_t          mWhen;
+    android_LogPriority mPriority;
+    pid_t           mPid;
+    char*           mTag;
+    char*           mMsg;
+
+    /* additional goodies */
+    int             mRefCnt;        // reference count
+    bool            mInternal;      // message generated internally by us?
+    int             mFootprint;     // approx. size of this object in memory
+    int             mTextCtrlLen;   // #of characters req'd in text ctrl
+    LogMessage*     mpPrev;         // link to previous item in log pool
+    LogMessage*     mpNext;         // link to next item in log pool
+};
+
+#endif // _SIM_LOG_MESSAGE_H
diff --git a/simulator/app/LogPool.cpp b/simulator/app/LogPool.cpp
new file mode 100644
index 0000000..43bf4be
--- /dev/null
+++ b/simulator/app/LogPool.cpp
@@ -0,0 +1,90 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Hold a collection of log messages, limiting ourselves to a certain
+// fixed maximum amount of memory.
+//
+#include "LogPool.h"
+#include <assert.h>
+
+
+/*
+ * Add a message at the head of the pool.
+ */
+void LogPool::Add(LogMessage* pLogMessage)
+{
+    pLogMessage->Acquire();     // bump up the ref count
+
+    assert(pLogMessage->GetPrev() == NULL);
+    assert(pLogMessage->GetNext() == NULL);
+
+    if (mpHead == NULL) {
+        assert(mpTail == NULL);
+        mpTail = mpHead = pLogMessage;
+    } else {
+        assert(mpHead->GetPrev() == NULL);
+        mpHead->SetPrev(pLogMessage);
+        pLogMessage->SetNext(mpHead);
+        mpHead = pLogMessage;
+    }
+
+    /* update the pool size, and remove old entries if necessary */
+    mCurrentSize += pLogMessage->GetFootprint();
+
+    while (mCurrentSize > mMaxSize)
+        RemoveOldest();
+}
+
+/*
+ * Remove the oldest message (from the tail of the list).
+ */
+void LogPool::RemoveOldest(void)
+{
+    LogMessage* pPrev;
+
+    if (mpTail == NULL) {
+        fprintf(stderr, "HEY: nothing left to remove (cur=%ld)\n",
+            mCurrentSize);
+        assert(false);
+        return;
+    }
+
+    if (mpTail == mpBookmark)
+        mpBookmark = NULL;
+
+    //printf("--- removing oldest, size %ld->%ld (%s)\n",
+    //    mCurrentSize, mCurrentSize - mpTail->GetFootprint(),mpTail->GetMsg());
+    mCurrentSize -= mpTail->GetFootprint();
+
+    pPrev = mpTail->GetPrev();
+    mpTail->Release();
+    mpTail = pPrev;
+    if (mpTail == NULL) {
+        //printf("--- pool is now empty (size=%ld)\n", mCurrentSize);
+        mpHead = NULL;
+    } else {
+        mpTail->SetNext(NULL);
+    }
+}
+
+/*
+ * Resize the log pool.
+ */
+void LogPool::Resize(long maxSize)
+{
+    assert(maxSize >= 0);
+
+    mMaxSize = maxSize;
+    while (mCurrentSize > mMaxSize)
+        RemoveOldest();
+}
+
+/*
+ * Remove all entries.
+ */
+void LogPool::Clear(void)
+{
+    while (mpTail != NULL)
+        RemoveOldest();
+}
+
diff --git a/simulator/app/LogPool.h b/simulator/app/LogPool.h
new file mode 100644
index 0000000..b824fec
--- /dev/null
+++ b/simulator/app/LogPool.h
@@ -0,0 +1,56 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Pool of log messages.  Not thread safe -- operations on the log pool
+// should only happen in the main UI thread.
+//
+#ifndef _SIM_LOG_POOL_H
+#define _SIM_LOG_POOL_H
+
+#include "LogMessage.h"
+
+/*
+ * This contains the pool of log messages.  The messages themselves are
+ * allocated individually and reference counted.  We add new messages to
+ * the head and, when the total "footprint" exceeds our stated max, we
+ * delete one or more from the tail.
+ *
+ * To support pause/resume, we allow a "bookmark" to be set.  This is
+ * just a pointer to a message in the pool.  If the bookmarked message
+ * is deleted, we discard the bookmark.
+ */
+class LogPool {
+public:
+    LogPool(void)
+        : mpHead(NULL), mpTail(NULL), mpBookmark(NULL),
+          mCurrentSize(0), mMaxSize(10240)
+        {}
+    ~LogPool(void) { Clear(); }
+
+    void Clear(void);
+
+    /* add a new message to the pool */
+    void Add(LogMessage* pLogMessage);
+
+    /* resize the pool, removing excess messages */
+    void Resize(long maxSize);
+
+    /* return the current limit, in bytes */
+    long GetMaxSize(void) const { return mMaxSize; }
+
+    LogMessage* GetHead(void) const { return mpHead; }
+
+    void SetBookmark(void) { mpBookmark = mpHead; }
+    LogMessage* GetBookmark(void) const { return mpBookmark; }
+
+private:
+    void RemoveOldest(void);
+
+    LogMessage*     mpHead;
+    LogMessage*     mpTail;
+    LogMessage*     mpBookmark;
+    long            mCurrentSize;       // current size, in bytes
+    long            mMaxSize;           // maximum size, in bytes
+};
+
+#endif // _SIM_LOG_POOL_H
diff --git a/simulator/app/LogPrefsDialog.cpp b/simulator/app/LogPrefsDialog.cpp
new file mode 100644
index 0000000..dc3e07c
--- /dev/null
+++ b/simulator/app/LogPrefsDialog.cpp
@@ -0,0 +1,404 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Log preferences modal dialog.
+//
+
+// 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 "LogPrefsDialog.h"
+#include "Preferences.h"
+#include "Resource.h"
+#include "utils.h"
+
+BEGIN_EVENT_TABLE(LogPrefsDialog, wxDialog)
+    EVT_CHECKBOX(IDC_LOG_PREFS_WRITE_FILE, LogPrefsDialog::OnWriteFile)
+END_EVENT_TABLE()
+
+static const wxString gSpacerChoices[] = { 
+    wxT("0"), wxT("1"), wxT("2") 
+};
+static const wxString gPointSizes[] = { 
+    wxT("4"), wxT("6"), wxT("8"), wxT("10"), wxT("12"), wxT("14"), wxT("16") 
+};
+
+
+/*
+ * Constructor.
+ */
+LogPrefsDialog::LogPrefsDialog(wxWindow* parent)
+    : wxDialog(parent, IDD_LOG_PREFS, wxT("Log Preferences"), wxDefaultPosition,
+        wxDefaultSize, wxDEFAULT_DIALOG_STYLE),
+      mHeaderFormat(kHFFull), mSingleLine(false), mExtraSpacing(0),
+      mUseColor(false), mFontMonospace(false), mDisplayMax(0), mPoolSizeKB(0)
+{
+    CreateControls();
+}
+
+
+/*
+ * Destructor.  Not much to do.
+ */
+LogPrefsDialog::~LogPrefsDialog(void)
+{
+}
+
+/*
+ * Create all of the pages and add them to the notebook.
+ */
+void LogPrefsDialog::CreateControls(void)
+{
+    wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
+    wxBoxSizer* okCancelSizer = new wxBoxSizer(wxHORIZONTAL);
+    mNotebook.Create(this, wxID_ANY);
+    wxPanel* page;
+
+    page = CreateFormatPage(&mNotebook);
+    mNotebook.AddPage(page, wxT("Format"), true);
+    page = CreateLimitsPage(&mNotebook);
+    mNotebook.AddPage(page, wxT("Limits"), false);
+    page = CreateFilesPage(&mNotebook);
+    mNotebook.AddPage(page, wxT("Files"), false);
+
+    // note to self: could use CreateButtonSizer here?
+    wxButton* cancel = new wxButton(this, wxID_CANCEL, wxT("&Cancel"),
+        wxDefaultPosition, wxDefaultSize, 0);
+    okCancelSizer->Add(cancel, 0, wxALL, kInterSpacing);
+
+    wxButton* ok = new wxButton(this, wxID_OK, wxT("&OK"),
+        wxDefaultPosition, wxDefaultSize, 0);
+    okCancelSizer->Add(ok, 0, wxALL, kInterSpacing);
+
+    mainSizer->Add(&mNotebook);
+    mainSizer->Add(okCancelSizer, 0, wxALIGN_RIGHT);
+
+    SetSizer(mainSizer);
+
+    mainSizer->Fit(this);           // shrink-to-fit
+    mainSizer->SetSizeHints(this);  // define minimum size
+}
+
+/*
+ * Transfer data from our members to the window controls.
+ */
+bool LogPrefsDialog::TransferDataToWindow(void)
+{
+    /*
+     * Do standard dialog setup.
+     */
+    wxRadioButton* fmtFull = (wxRadioButton*) FindWindow(IDC_LOG_PREFS_FMT_FULL);
+    wxRadioButton* fmtBrief = (wxRadioButton*) FindWindow(IDC_LOG_PREFS_FMT_BRIEF);
+    wxRadioButton* fmtMinimal = (wxRadioButton*) FindWindow(IDC_LOG_PREFS_FMT_MINIMAL);
+    wxCheckBox* singleLine = (wxCheckBox*) FindWindow(IDC_LOG_PREFS_SINGLE_LINE);
+    wxComboBox* extraSpacing = (wxComboBox*) FindWindow(IDC_LOG_PREFS_EXTRA_SPACING);
+    wxComboBox* pointSize = (wxComboBox*) FindWindow(IDC_LOG_PREFS_POINT_SIZE);
+    wxCheckBox* useColor = (wxCheckBox*) FindWindow(IDC_LOG_PREFS_USE_COLOR);
+    wxCheckBox* fontMono = (wxCheckBox*) FindWindow(IDC_LOG_PREFS_FONT_MONO);
+    // -
+    wxTextCtrl* displayMax = (wxTextCtrl*) FindWindow(IDC_LOG_PREFS_DISPLAY_MAX);
+    wxTextCtrl* poolSize = (wxTextCtrl*) FindWindow(IDC_LOG_PREFS_POOL_SIZE);
+    // -
+    wxCheckBox* writeFile = (wxCheckBox*) FindWindow(IDC_LOG_PREFS_WRITE_FILE);
+    wxTextCtrl* fileName = (wxTextCtrl*) FindWindow(IDC_LOG_PREFS_FILENAME);
+    wxCheckBox* truncateOld = (wxCheckBox*) FindWindow(IDC_LOG_PREFS_TRUNCATE_OLD);
+    // -
+
+    fmtFull->SetValue(mHeaderFormat == kHFFull);
+    fmtBrief->SetValue(mHeaderFormat == kHFBrief);
+    fmtMinimal->SetValue(mHeaderFormat == kHFMinimal);
+    singleLine->SetValue(mSingleLine);
+    if (mExtraSpacing < 0 || mExtraSpacing > NELEM(gSpacerChoices))
+        mExtraSpacing = 0;
+    extraSpacing->SetSelection(mExtraSpacing);
+
+    pointSize->SetSelection(0);
+    for (int i = 0; i < NELEM(gPointSizes); i++) {
+        if (atoi(gPointSizes[i].ToAscii()) == mPointSize) {
+            pointSize->SetSelection(i);
+            break;
+        }
+    }
+    useColor->SetValue(mUseColor);
+    fontMono->SetValue(mFontMonospace);
+
+    wxString tmpStr;
+    tmpStr.Printf(wxT("%d"), mDisplayMax);
+    displayMax->SetValue(tmpStr);
+    tmpStr.Printf(wxT("%d"), mPoolSizeKB);
+    poolSize->SetValue(tmpStr);
+
+    writeFile->SetValue(mWriteFile);
+    fileName->SetValue(mFileName);
+    truncateOld->SetValue(mTruncateOld);
+
+    EnableFileControls(mWriteFile);
+
+    return true;
+}
+
+/*
+ * Convert a string to a number.  The number is expected to be unsigned.
+ * Returns < 0 on failure.
+ */
+static long ConvertUnsigned(const wxString& str)
+{
+    long val;
+    if (!str.ToLong(&val))
+        return -1;
+    return val;
+}
+
+/*
+ * Transfer and validate data from the window controls.
+ *
+ * This doesn't get called if the user cancels out of the dialog.
+ */
+bool LogPrefsDialog::TransferDataFromWindow(void)
+{
+    /*
+     * Do standard dialog export.
+     */
+    //wxRadioButton* fmtFull = (wxRadioButton*) FindWindow(IDC_LOG_PREFS_FMT_FULL);
+    wxRadioButton* fmtBrief = (wxRadioButton*) FindWindow(IDC_LOG_PREFS_FMT_BRIEF);
+    wxRadioButton* fmtMinimal = (wxRadioButton*) FindWindow(IDC_LOG_PREFS_FMT_MINIMAL);
+    wxCheckBox* singleLine = (wxCheckBox*) FindWindow(IDC_LOG_PREFS_SINGLE_LINE);
+    wxComboBox* extraSpacing = (wxComboBox*) FindWindow(IDC_LOG_PREFS_EXTRA_SPACING);
+    wxComboBox* pointSize = (wxComboBox*) FindWindow(IDC_LOG_PREFS_POINT_SIZE);
+    wxCheckBox* useColor = (wxCheckBox*) FindWindow(IDC_LOG_PREFS_USE_COLOR);
+    wxCheckBox* fontMono = (wxCheckBox*) FindWindow(IDC_LOG_PREFS_FONT_MONO);
+    // -
+    wxTextCtrl* displayMax = (wxTextCtrl*) FindWindow(IDC_LOG_PREFS_DISPLAY_MAX);
+    wxTextCtrl* poolSize = (wxTextCtrl*) FindWindow(IDC_LOG_PREFS_POOL_SIZE);
+    // -
+    wxCheckBox* writeFile = (wxCheckBox*) FindWindow(IDC_LOG_PREFS_WRITE_FILE);
+    wxTextCtrl* fileName = (wxTextCtrl*) FindWindow(IDC_LOG_PREFS_FILENAME);
+    wxCheckBox* truncateOld = (wxCheckBox*) FindWindow(IDC_LOG_PREFS_TRUNCATE_OLD);
+    // -
+
+    mHeaderFormat = kHFFull;
+    if (fmtBrief->GetValue())
+        mHeaderFormat = kHFBrief;
+    else if (fmtMinimal->GetValue())
+        mHeaderFormat = kHFMinimal;
+
+    wxString tmpStr;
+
+    mSingleLine = (singleLine->GetValue() != 0);
+    mExtraSpacing = extraSpacing->GetSelection();
+    mPointSize = ConvertUnsigned(pointSize->GetValue());
+    mUseColor = useColor->GetValue();
+    mFontMonospace = fontMono->GetValue();
+
+    tmpStr = displayMax->GetValue();
+    mDisplayMax = ConvertUnsigned(tmpStr);
+    if (mDisplayMax <= 0 || mDisplayMax > 1000 * 1000) {
+        wxMessageBox(wxT("Bad value for display max -- must be > 0 and <= 1,000,000"),
+            wxT("Hoser"), wxOK, this);
+        return false;
+    }
+
+    tmpStr = poolSize->GetValue();
+    mPoolSizeKB = ConvertUnsigned(tmpStr);
+    if (mDisplayMax <= 0 || mDisplayMax > 1048576) {
+        wxMessageBox(wxT("Bad value for pool size -- must be > 0 and <= 1048576"),
+            wxT("Hoser"), wxOK, this);
+        return false;
+    }
+
+    mWriteFile = (writeFile->GetValue() != 0);
+    mFileName = fileName->GetValue();
+    mTruncateOld = (truncateOld->GetValue() != 0);
+    if (mWriteFile && mFileName.IsEmpty()) {
+        wxMessageBox(wxT("Log filename may not be blank"),
+            wxT("Hoser"), wxOK, this);
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Create the log Format page.
+ */
+wxPanel* LogPrefsDialog::CreateFormatPage(wxBookCtrlBase* parent)
+{
+    wxPanel* panel = new wxPanel(parent);
+
+    wxStaticBoxSizer* headerOpts = new wxStaticBoxSizer(wxVERTICAL, panel,
+        wxT("Header"));
+    headerOpts->Add(new wxRadioButton(panel, IDC_LOG_PREFS_FMT_FULL,
+            wxT("Full header"), wxDefaultPosition, wxDefaultSize,
+            wxRB_GROUP));
+    headerOpts->Add(new wxRadioButton(panel, IDC_LOG_PREFS_FMT_BRIEF,
+            wxT("Brief header")));
+    headerOpts->Add(new wxRadioButton(panel, IDC_LOG_PREFS_FMT_MINIMAL,
+            wxT("Minimal, integrated header")));
+
+    wxCheckBox* singleLine = new wxCheckBox(panel, IDC_LOG_PREFS_SINGLE_LINE,
+        wxT("Put headers and message on same line"));
+
+    wxStaticText* extraSpacingDescr = new wxStaticText(panel, wxID_STATIC,
+        wxT("Extra line spacing:"));
+    wxComboBox* extraSpacing = new wxComboBox(panel,
+        IDC_LOG_PREFS_EXTRA_SPACING, wxT("blah"),
+        wxDefaultPosition, wxDefaultSize, NELEM(gSpacerChoices),
+        gSpacerChoices, wxCB_READONLY);
+    wxBoxSizer* extraSpacingSizer = new wxBoxSizer(wxHORIZONTAL);
+    extraSpacingSizer->Add(extraSpacingDescr, 0, wxALIGN_CENTER_VERTICAL);
+    extraSpacingSizer->AddSpacer(kInterSpacing);
+    extraSpacingSizer->Add(extraSpacing);
+
+    wxStaticBoxSizer* textOpts = new wxStaticBoxSizer(wxVERTICAL, panel,
+        wxT("Text"));
+    textOpts->Add(
+            new wxStaticText(panel, wxID_STATIC, wxT("Point size:")) );
+    textOpts->AddSpacer(kInterSpacing);
+    textOpts->Add(
+        new wxComboBox(panel,
+            IDC_LOG_PREFS_POINT_SIZE, wxT("blah"),
+            wxDefaultPosition, wxDefaultSize, NELEM(gPointSizes),
+            gPointSizes, wxCB_READONLY) );
+    textOpts->AddSpacer(kInterSpacing);
+    textOpts->Add(
+            new wxCheckBox(panel, IDC_LOG_PREFS_USE_COLOR,
+                wxT("Colorful messages")) );
+    textOpts->AddSpacer(kInterSpacing);
+    textOpts->Add(
+            new wxCheckBox(panel, IDC_LOG_PREFS_FONT_MONO,
+                wxT("Use monospace font")) );
+
+
+    wxBoxSizer* sizerPanel = new wxBoxSizer(wxVERTICAL);
+    sizerPanel->Add(kMinWidth, kEdgeSpacing);       // forces minimum width
+    sizerPanel->Add(headerOpts);
+    sizerPanel->AddSpacer(kInterSpacing);
+    sizerPanel->Add(singleLine);
+    sizerPanel->AddSpacer(kInterSpacing);
+    sizerPanel->Add(extraSpacingSizer);
+    sizerPanel->AddSpacer(kInterSpacing);
+    sizerPanel->Add(textOpts);
+    sizerPanel->AddSpacer(kInterSpacing);
+
+    wxBoxSizer* horizIndent = new wxBoxSizer(wxHORIZONTAL);
+    horizIndent->AddSpacer(kEdgeSpacing);
+    horizIndent->Add(sizerPanel);
+    horizIndent->AddSpacer(kEdgeSpacing);
+    panel->SetSizer(horizIndent);
+
+    return panel;
+}
+
+/*
+ * Create the log Limits page.
+ */
+wxPanel* LogPrefsDialog::CreateLimitsPage(wxBookCtrlBase* parent)
+{
+    wxPanel* panel = new wxPanel(parent);
+
+    wxBoxSizer* displayMaxSizer = new wxBoxSizer(wxHORIZONTAL);
+    displayMaxSizer->Add(
+            new wxStaticText(panel, wxID_ANY,
+                wxT("Maximum entries in log window:"),
+                wxDefaultPosition, wxDefaultSize,
+                wxALIGN_LEFT),
+            0, wxALIGN_CENTER_VERTICAL);
+    displayMaxSizer->AddSpacer(kInterSpacing);
+    displayMaxSizer->Add(
+            new wxTextCtrl(panel, IDC_LOG_PREFS_DISPLAY_MAX));
+
+    wxBoxSizer* poolSizeSizer = new wxBoxSizer(wxHORIZONTAL);
+    poolSizeSizer->Add(
+            new wxStaticText(panel, wxID_ANY,
+                wxT("Size of the log pool (KB):"),
+                wxDefaultPosition, wxDefaultSize,
+                wxALIGN_LEFT),
+            0, wxALIGN_CENTER_VERTICAL);
+    poolSizeSizer->AddSpacer(kInterSpacing);
+    poolSizeSizer->Add(
+            new wxTextCtrl(panel, IDC_LOG_PREFS_POOL_SIZE));
+
+
+    wxBoxSizer* sizerPanel = new wxBoxSizer(wxVERTICAL);
+    sizerPanel->Add(kMinWidth, kEdgeSpacing);       // forces minimum width
+    sizerPanel->Add(displayMaxSizer);
+    sizerPanel->AddSpacer(kInterSpacing);
+    sizerPanel->Add(poolSizeSizer);
+    sizerPanel->AddSpacer(kInterSpacing);
+
+    wxBoxSizer* horizIndent = new wxBoxSizer(wxHORIZONTAL);
+    horizIndent->AddSpacer(kEdgeSpacing);
+    horizIndent->Add(sizerPanel);
+    horizIndent->AddSpacer(kEdgeSpacing);
+    panel->SetSizer(horizIndent);
+
+    return panel;
+}
+
+/*
+ * Create the log Files page.
+ */
+wxPanel* LogPrefsDialog::CreateFilesPage(wxBookCtrlBase* parent)
+{
+    wxPanel* panel = new wxPanel(parent);
+    wxStaticBoxSizer* logOpts = new wxStaticBoxSizer(wxVERTICAL, panel,
+        wxT("Log File"));
+
+    wxCheckBox* writeCopy =
+            new wxCheckBox(panel, IDC_LOG_PREFS_WRITE_FILE,
+                wxT("Write a copy of log output to a file"));
+
+    logOpts->AddSpacer(kInterSpacing);
+    logOpts->Add(
+            new wxStaticText(panel, wxID_ANY,
+                wxT("Filename:"),
+                wxDefaultPosition, wxDefaultSize,
+                wxALIGN_LEFT));
+    logOpts->AddSpacer(kInterSpacing);
+    logOpts->Add(
+            new wxTextCtrl(panel, IDC_LOG_PREFS_FILENAME), 0, wxEXPAND);
+    logOpts->AddSpacer(kInterSpacing);
+    logOpts->Add(
+            new wxCheckBox(panel, IDC_LOG_PREFS_TRUNCATE_OLD,
+                wxT("Truncate the file if more than 8 hours old ")) );
+
+
+    wxBoxSizer* sizerPanel = new wxBoxSizer(wxVERTICAL);
+    sizerPanel->Add(kMinWidth, kEdgeSpacing);       // forces minimum width
+    sizerPanel->Add(writeCopy);
+    sizerPanel->AddSpacer(kInterSpacing);
+    sizerPanel->Add(logOpts);
+    sizerPanel->AddSpacer(kInterSpacing);
+
+    wxBoxSizer* horizIndent = new wxBoxSizer(wxHORIZONTAL);
+    horizIndent->AddSpacer(kEdgeSpacing);
+    horizIndent->Add(sizerPanel);
+    horizIndent->AddSpacer(kEdgeSpacing);
+    panel->SetSizer(horizIndent);
+
+    return panel;
+}
+
+
+/*
+ * Handle clicks on the "write file" checkbox.
+ */
+void LogPrefsDialog::OnWriteFile(wxCommandEvent& event)
+{
+    EnableFileControls(event.GetInt());
+}
+
+/*
+ * Enable or disable some of the controls on the "file" page.
+ */
+void LogPrefsDialog::EnableFileControls(bool enable)
+{
+    FindWindow(IDC_LOG_PREFS_FILENAME)->Enable(enable);
+    FindWindow(IDC_LOG_PREFS_TRUNCATE_OLD)->Enable(enable);
+}
+
diff --git a/simulator/app/LogPrefsDialog.h b/simulator/app/LogPrefsDialog.h
new file mode 100644
index 0000000..17a73a3
--- /dev/null
+++ b/simulator/app/LogPrefsDialog.h
@@ -0,0 +1,70 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Log preferences modal dialog.
+//
+#ifndef _SIM_LOG_PREFS_DIALOG_H
+#define _SIM_LOG_PREFS_DIALOG_H
+
+/*
+ * Declaration of log preferences dialog.  This class defines the outer
+ * wrapper as well as all of the pages.
+ */
+class LogPrefsDialog : public wxDialog {
+    DECLARE_EVENT_TABLE()
+
+public:
+    LogPrefsDialog(wxWindow* parent);
+    virtual ~LogPrefsDialog(void);
+
+    void CreateControls(void);
+
+    /* these correspond to radio buttons */
+    typedef enum HeaderFormat {
+        kHFFull = 0,
+        kHFBrief,
+        kHFMinimal,
+        kHFInternal,        // special -- used for internally generated msgs
+    };
+
+    /*
+     * Values edited in the preference pages.  By Windows convention,
+     * these are public.
+     */
+    /* format options */
+    HeaderFormat mHeaderFormat;
+    bool        mSingleLine;        // put whole message on one line?
+    int         mExtraSpacing;      // double/triple-space messages?
+    int         mPointSize;         // text size
+    bool        mUseColor;          // colorful messages?
+    bool        mFontMonospace;     // use monospace font?
+
+    /* limit options */
+    int         mDisplayMax;
+    int         mPoolSizeKB;
+
+    /* file options */
+    bool        mWriteFile;
+    wxString    mFileName;
+    bool        mTruncateOld;
+
+private:
+    bool TransferDataToWindow(void);
+    bool TransferDataFromWindow(void);
+
+    wxPanel* CreateFormatPage(wxBookCtrlBase* parent);
+    wxPanel* CreateLimitsPage(wxBookCtrlBase* parent);
+    wxPanel* CreateFilesPage(wxBookCtrlBase* parent);
+
+    void OnWriteFile(wxCommandEvent& event);
+    void EnableFileControls(bool enable);
+
+    /* main notebook; for aesthetic reasons we may want a Choicebook */
+    wxNotebook    mNotebook;
+
+    enum {
+        kMinWidth = 300,        // minimum prefs dialog width, in pixels
+    };
+};
+
+#endif // _SIM_LOG_PREFS_DIALOG_H
diff --git a/simulator/app/LogWindow.cpp b/simulator/app/LogWindow.cpp
new file mode 100644
index 0000000..53ba9d0
--- /dev/null
+++ b/simulator/app/LogWindow.cpp
@@ -0,0 +1,1156 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Display runtime log output.
+//
+
+// 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"   // needed for Windows build
+#include "wx/dcbuffer.h"
+
+#include "LogWindow.h"
+#include "LogMessage.h"
+#include "LogPrefsDialog.h"
+#include "MyApp.h"
+#include "Preferences.h"
+#include "Resource.h"
+#include "UserEventMessage.h"
+
+#include <errno.h>
+
+static int android_snprintfBuffer(char** pBuf, int bufLen, const char* format, ...);
+static int android_vsnprintfBuffer(char** pBuf, int bufLen, const char* format, va_list args);
+
+
+using namespace android;
+
+#if 0   // experiment -- works on Win32, but not with GTK
+class MyTextCtrl : public wxTextCtrl {
+public:
+    MyTextCtrl(wxWindow* parent, wxWindowID id, const wxString& value,
+        const wxPoint& pos, const wxSize& size, int style = 0)
+        : wxTextCtrl(parent, id, value, pos, size, style)
+        {
+            printf("***************** MyTextCtrl!\n");
+        }
+
+    void OnScroll(wxScrollWinEvent& event);
+    void OnScrollBottom(wxScrollWinEvent& event);
+
+private:
+    DECLARE_EVENT_TABLE()
+};
+
+BEGIN_EVENT_TABLE(MyTextCtrl, wxTextCtrl)
+    EVT_SCROLLWIN(MyTextCtrl::OnScroll)
+    EVT_SCROLLWIN_BOTTOM(MyTextCtrl::OnScrollBottom)
+END_EVENT_TABLE()
+
+void MyTextCtrl::OnScroll(wxScrollWinEvent& event)
+{
+    printf("OnScroll!\n");
+}
+
+void MyTextCtrl::OnScrollBottom(wxScrollWinEvent& event)
+{
+    printf("OnScrollBottom!\n");
+}
+#endif
+
+
+BEGIN_EVENT_TABLE(LogWindow, wxDialog)
+    EVT_CLOSE(LogWindow::OnClose)
+    EVT_MOVE(LogWindow::OnMove)
+    EVT_COMBOBOX(IDC_LOG_LEVEL, LogWindow::OnLogLevel)
+    EVT_BUTTON(IDC_LOG_CLEAR, LogWindow::OnLogClear)
+    EVT_BUTTON(IDC_LOG_PAUSE, LogWindow::OnLogPause)
+    EVT_BUTTON(IDC_LOG_PREFS, LogWindow::OnLogPrefs)
+END_EVENT_TABLE()
+
+/*
+ * Information about log levels.
+ *
+ * Each entry here corresponds to an entry in the combo box.  The first
+ * letter of each name should be unique.
+ */
+static const struct {
+    wxString    name;
+    android_LogPriority priority;
+} gLogLevels[] = {
+    { wxT("Verbose"),    ANDROID_LOG_VERBOSE },
+    { wxT("Debug"),      ANDROID_LOG_DEBUG },
+    { wxT("Info"),       ANDROID_LOG_INFO },
+    { wxT("Warn"),       ANDROID_LOG_WARN },
+    { wxT("Error"),      ANDROID_LOG_ERROR }
+};
+
+
+/*
+ * Create a new LogWindow.  This should be a child of the main frame.
+ */
+LogWindow::LogWindow(wxWindow* parent)
+    : wxDialog(parent, wxID_ANY, wxT("Log Output"), wxDefaultPosition,
+        wxDefaultSize,
+        wxCAPTION | wxSYSTEM_MENU | wxCLOSE_BOX | wxRESIZE_BORDER),
+      mDisplayArray(NULL), mMaxDisplayMsgs(0), mPaused(false),
+      mMinPriority(ANDROID_LOG_VERBOSE),
+      mHeaderFormat(LogPrefsDialog::kHFFull),
+      mSingleLine(false), mExtraSpacing(0), mPointSize(10), mUseColor(true),
+      mFontMonospace(true), mWriteFile(false), mTruncateOld(true), mLogFp(NULL),
+      mNewlyShown(false), mLastPosition(wxDefaultPosition), mVisible(false)
+{
+    ConstructControls();
+
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+
+    int poolSize = 10240;       // 10MB
+    pPrefs->GetInt("log-pool-size-kbytes", &poolSize);
+    assert(poolSize > 0);
+    mPool.Resize(poolSize * 1024);
+
+    mMaxDisplayMsgs = 1000;
+    pPrefs->GetInt("log-display-msg-count", &mMaxDisplayMsgs);
+    assert(mMaxDisplayMsgs > 0);
+    mDisplayArray = new LogMessage*[mMaxDisplayMsgs];
+    memset(mDisplayArray, 0, sizeof(LogMessage*) * mMaxDisplayMsgs);
+    mTopPtr = -1;
+    mNextPtr = 0;
+
+    int tmpInt = (int) mHeaderFormat;
+    pPrefs->GetInt("log-header-format", &tmpInt);
+    mHeaderFormat = (LogPrefsDialog::HeaderFormat) tmpInt;
+    pPrefs->GetBool("log-single-line", &mSingleLine);
+    pPrefs->GetInt("log-extra-spacing", &mExtraSpacing);
+    pPrefs->GetInt("log-point-size", &mPointSize);
+    pPrefs->GetBool("log-use-color", &mUseColor);
+    pPrefs->SetBool("log-font-monospace", &mFontMonospace);
+    SetTextStyle();
+
+    mFileName = wxT("/tmp/android-log.txt");
+    pPrefs->GetBool("log-write-file", &mWriteFile);
+    pPrefs->GetString("log-filename", /*ref*/mFileName);
+    pPrefs->GetBool("log-truncate-old", &mTruncateOld);
+
+    PrepareLogFile();
+}
+
+/*
+ * Destroy everything we own.
+ */
+LogWindow::~LogWindow(void)
+{
+    ClearDisplay();
+    delete[] mDisplayArray;
+
+    if (mLogFp != NULL)
+        fclose(mLogFp);
+}
+
+/*
+ * Set the text style, based on our preferences.
+ */
+void LogWindow::SetTextStyle(void)
+{
+    wxTextCtrl* pTextCtrl;
+    pTextCtrl = (wxTextCtrl*) FindWindow(IDC_LOG_TEXT);
+    wxTextAttr style;
+    style = pTextCtrl->GetDefaultStyle();
+
+    if (mFontMonospace) {
+        wxFont font(mPointSize, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL,
+            wxFONTWEIGHT_NORMAL);
+        style.SetFont(font);
+    } else {
+        wxFont font(mPointSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
+            wxFONTWEIGHT_NORMAL);
+        style.SetFont(font);
+    }
+
+    pTextCtrl->SetDefaultStyle(style);
+}
+
+/*
+ * Set up the goodies in the window.
+ *
+ * Also initializes mMinPriority.
+ */
+void LogWindow::ConstructControls(void)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    wxPanel* base = new wxPanel(this, wxID_ANY);
+    wxBoxSizer* masterSizer = new wxBoxSizer(wxVERTICAL);
+    wxBoxSizer* indentSizer = new wxBoxSizer(wxHORIZONTAL);
+    wxBoxSizer* configPrioritySizer = new wxBoxSizer(wxHORIZONTAL);
+    wxGridSizer* configSizer = new wxGridSizer(4, 1);
+
+    /*
+     * Configure log level combo box.
+     */
+    wxComboBox* logLevel;
+    int defaultLogLevel = 1;
+    pPrefs->GetInt("log-display-level", &defaultLogLevel);
+    logLevel = new wxComboBox(base, IDC_LOG_LEVEL, wxT(""),
+        wxDefaultPosition, wxDefaultSize, 0, NULL,
+        wxCB_READONLY /*| wxSUNKEN_BORDER*/);
+    for (int i = 0; i < NELEM(gLogLevels); i++) {
+        logLevel->Append(gLogLevels[i].name);
+        logLevel->SetClientData(i, (void*) gLogLevels[i].priority);
+    }
+    logLevel->SetSelection(defaultLogLevel);
+    mMinPriority = gLogLevels[defaultLogLevel].priority;
+
+    /*
+     * Set up stuff at the bottom, starting with the options
+     * at the bottom left.
+     */
+    configPrioritySizer->Add(new wxStaticText(base, wxID_ANY, wxT("Log level:"),
+            wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT),
+        0, wxALIGN_CENTER_VERTICAL);
+    configPrioritySizer->AddSpacer(kInterSpacing);
+    configPrioritySizer->Add(logLevel);
+
+    wxButton* clear = new wxButton(base, IDC_LOG_CLEAR, wxT("&Clear"),
+        wxDefaultPosition, wxDefaultSize, 0);
+    wxButton* pause = new wxButton(base, IDC_LOG_PAUSE, wxT("&Pause"),
+        wxDefaultPosition, wxDefaultSize, 0);
+    wxButton* prefs = new wxButton(base, IDC_LOG_PREFS, wxT("C&onfigure"),
+        wxDefaultPosition, wxDefaultSize, 0);
+
+    configSizer->Add(configPrioritySizer, 0, wxALIGN_LEFT);
+    configSizer->Add(clear, 0, wxALIGN_CENTER);
+    configSizer->Add(pause, 0, wxALIGN_CENTER);
+    configSizer->Add(prefs, 0, wxALIGN_RIGHT);
+
+    /*
+     * Create text ctrl.
+     */
+    wxTextCtrl* pTextCtrl;
+    pTextCtrl = new wxTextCtrl(base, IDC_LOG_TEXT, wxT(""),
+        wxDefaultPosition, wxDefaultSize,
+        wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2 | wxTE_NOHIDESEL |
+            wxHSCROLL);
+
+    /*
+     * Add components to master sizer.
+     */
+    masterSizer->AddSpacer(kEdgeSpacing);
+    masterSizer->Add(pTextCtrl, 1, wxEXPAND);
+    masterSizer->AddSpacer(kInterSpacing);
+    masterSizer->Add(configSizer, 0, wxEXPAND);
+    masterSizer->AddSpacer(kEdgeSpacing);
+
+    /*
+     * Indent from sides.
+     */
+    indentSizer->AddSpacer(kEdgeSpacing);
+    indentSizer->Add(masterSizer, 1, wxEXPAND);
+    indentSizer->AddSpacer(kEdgeSpacing);
+
+    base->SetSizer(indentSizer);
+
+    indentSizer->Fit(this);             // shrink-to-fit
+    indentSizer->SetSizeHints(this);    // define minimum size
+}
+
+/*
+ * In some cases, this means the user has clicked on our "close" button.
+ * We don't really even want one, but both WinXP and KDE put one on our
+ * window whether we want it or not.  So, we make it work as a "hide"
+ * button instead.
+ *
+ * This also gets called when the app is shutting down, and we do want
+ * to destroy ourselves then, saving various information about our state.
+ */
+void LogWindow::OnClose(wxCloseEvent& event)
+{
+    /* just hide the window, unless we're shutting down */
+    if (event.CanVeto()) {
+        event.Veto();
+        Show(false);
+        return;
+    }
+
+    /*
+     * Save some preferences.
+     */
+    SaveWindowPrefs();
+
+    /* if we can't veto the Close(), destroy ourselves */
+    Destroy();
+}
+
+/*
+ * Save all of our preferences to the config file.
+ */
+void LogWindow::SaveWindowPrefs(void)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+
+    /*
+     * Save shown/hidden state.
+     */
+    pPrefs->SetBool("window-log-show", IsShown());
+
+    /*
+     * Limits and formatting prefs.
+     */
+    pPrefs->SetInt("log-display-msg-count", mMaxDisplayMsgs);
+    pPrefs->SetInt("log-pool-size-kbytes", mPool.GetMaxSize() / 1024);
+
+    pPrefs->SetInt("log-header-format", mHeaderFormat);
+    pPrefs->SetBool("log-single-line", mSingleLine);
+    pPrefs->SetInt("log-extra-spacing", mExtraSpacing);
+    pPrefs->SetInt("log-point-size", mPointSize);
+    pPrefs->SetBool("log-use-color", mUseColor);
+    pPrefs->SetBool("log-font-monospace", mFontMonospace);
+
+    pPrefs->SetBool("log-write-file", mWriteFile);
+    pPrefs->SetString("log-filename", mFileName.ToAscii());
+    pPrefs->SetBool("log-truncate-old", mTruncateOld);
+
+    /*
+     * Save window size and position.
+     */
+    wxPoint posn;
+    wxSize size;
+
+    assert(pPrefs != NULL);
+
+    posn = GetPosition();
+    size = GetSize();
+
+    pPrefs->SetInt("window-log-x", posn.x);
+    pPrefs->SetInt("window-log-y", posn.y);
+    pPrefs->SetInt("window-log-width", size.GetWidth());
+    pPrefs->SetInt("window-log-height", size.GetHeight());
+
+    /*
+     * Save current setting of debug level combo box.
+     */
+    wxComboBox* pCombo;
+    int selection;
+    pCombo = (wxComboBox*) FindWindow(IDC_LOG_LEVEL);
+    selection = pCombo->GetSelection();
+    pPrefs->SetInt("log-display-level", selection);
+}
+
+/*
+ * Return the desired position and size.
+ */
+/*static*/ wxRect LogWindow::GetPrefWindowRect(void)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    int x, y, width, height;
+
+    assert(pPrefs != NULL);
+
+    x = y = 10;
+    width = 500;
+    height = 200;
+
+    /* these don't modify the arg if the pref doesn't exist */
+    pPrefs->GetInt("window-log-x", &x);
+    pPrefs->GetInt("window-log-y", &y);
+    pPrefs->GetInt("window-log-width", &width);
+    pPrefs->GetInt("window-log-height", &height);
+
+    return wxRect(x, y, width, height);
+}
+
+/*
+ * Under Linux+GTK, the first time you show the window, it appears where
+ * it's supposed to.  If you then hide it and show it again, it gets
+ * moved on top of the parent window.  After that, you can reposition it
+ * and it remembers its position across hide/show.
+ *
+ * To counter this annoyance, we save the position when we hide, and
+ * reset the position after a show.  The "newly shown" flag ensures that
+ * we only reposition the window as the result of a Show(true) call.
+ *
+ * Sometimes, something helpful will shift the window over if it's
+ * partially straddling a seam between two monitors.  I don't see an easy
+ * way to block this, and I'm not sure I want to anyway.
+ */
+void LogWindow::OnMove(wxMoveEvent& event)
+{
+    wxPoint point;
+    point = event.GetPosition();
+    //printf("Sim: log window is at (%d,%d) (new=%d)\n", point.x, point.y,
+    //    mNewlyShown);
+
+    if (mNewlyShown) {
+        if (mLastPosition == wxDefaultPosition) {
+            //printf("Sim: no last position established\n");
+        } else {
+            Move(mLastPosition);
+        }
+
+        mNewlyShown = false;
+    }
+}
+
+/*
+ * Set the "newly shown" flag.
+ */
+bool LogWindow::Show(bool show)
+{
+    if (show) {
+        mNewlyShown = true;
+        Redisplay();
+    } else {
+        mLastPosition = GetPosition();
+    }
+
+    mVisible = show;
+    return wxDialog::Show(show);
+}
+
+/*
+ * User has adjusted the log level.  Update the display appropriately.
+ *
+ * This is a wxEVT_COMMAND_COMBOBOX_SELECTED event.
+ */
+void LogWindow::OnLogLevel(wxCommandEvent& event)
+{
+    int selection;
+    android_LogPriority priority;
+
+    selection = event.GetInt();
+    wxComboBox* pCombo = (wxComboBox*) FindWindow(IDC_LOG_LEVEL);
+    priority = (android_LogPriority) (int)pCombo->GetClientData(event.GetInt());
+
+    printf("Sim: log level selected: %d (%s)\n", (int) priority,
+        (const char*) gLogLevels[selection].name.ToAscii());
+    mMinPriority = priority;
+    Redisplay();
+}
+
+/*
+ * Clear out the log.
+ */
+void LogWindow::OnLogClear(wxCommandEvent& event)
+{
+    ClearDisplay();
+    mPool.Clear();
+}
+
+/*
+ * Handle the pause/resume button.
+ *
+ * If we're un-pausing, we need to get caught up.
+ */
+void LogWindow::OnLogPause(wxCommandEvent& event)
+{
+    mPaused = !mPaused;
+
+    wxButton* pButton = (wxButton*) FindWindow(IDC_LOG_PAUSE);
+    if (mPaused) {
+        pButton->SetLabel(wxT("&Resume"));
+
+        mPool.SetBookmark();
+    } else {
+        pButton->SetLabel(wxT("&Pause"));
+
+        LogMessage* pMsg = mPool.GetBookmark();
+        if (pMsg == NULL) {
+            /* bookmarked item fell out of pool */
+            printf("--- bookmark was lost, redisplaying\n");
+            Redisplay();
+        } else {
+            /*
+             * The bookmark points to the last item added to the display.
+             * We want to chase its "prev" pointer to walk toward the head
+             * of the list, adding items from oldest to newest.
+             */
+            pMsg = pMsg->GetPrev();
+            while (pMsg != NULL) {
+                if (FilterMatches(pMsg))
+                    AddToDisplay(pMsg);
+                pMsg = pMsg->GetPrev();
+            }
+        }
+    }
+}
+
+/*
+ * Open log preferences dialog.
+ */
+void LogWindow::OnLogPrefs(wxCommandEvent& event)
+{
+    LogPrefsDialog dialog(this);
+
+    /*
+     * Set up the dialog.
+     */
+    dialog.mHeaderFormat = mHeaderFormat;
+    dialog.mSingleLine = mSingleLine;
+    dialog.mExtraSpacing = mExtraSpacing;
+    dialog.mPointSize = mPointSize;
+    dialog.mUseColor = mUseColor;
+    dialog.mFontMonospace = mFontMonospace;
+
+    dialog.mDisplayMax = mMaxDisplayMsgs;
+    dialog.mPoolSizeKB = mPool.GetMaxSize() / 1024;
+
+    dialog.mWriteFile = mWriteFile;
+    dialog.mFileName = mFileName;
+    dialog.mTruncateOld = mTruncateOld;
+
+    /*
+     * Show it.  If they hit "OK", copy the updated values out, and
+     * re-display the log output.
+     */
+    if (dialog.ShowModal() == wxID_OK) {
+        /* discard old display arra */
+        ClearDisplay();
+        delete[] mDisplayArray;
+
+        mHeaderFormat = dialog.mHeaderFormat;
+        mSingleLine = dialog.mSingleLine;
+        mExtraSpacing = dialog.mExtraSpacing;
+        mPointSize = dialog.mPointSize;
+        mUseColor = dialog.mUseColor;
+        mFontMonospace = dialog.mFontMonospace;
+
+        assert(dialog.mDisplayMax > 0);
+        assert(dialog.mPoolSizeKB > 0);
+        mMaxDisplayMsgs = dialog.mDisplayMax;
+        mPool.Resize(dialog.mPoolSizeKB * 1024);
+
+        mWriteFile = dialog.mWriteFile;
+        if (mLogFp != NULL && mFileName != dialog.mFileName) {
+            printf("--- log file name changed, closing\n");
+            fclose(mLogFp);
+            mLogFp = NULL;
+        }
+        mFileName = dialog.mFileName;
+        mTruncateOld = dialog.mTruncateOld;
+
+        mDisplayArray = new LogMessage*[mMaxDisplayMsgs];
+        memset(mDisplayArray, 0, sizeof(LogMessage*) * mMaxDisplayMsgs);
+        Redisplay();
+
+        PrepareLogFile();
+    }
+}
+
+/*
+ * Handle a log message "user event".  This should only be called in
+ * the main UI thread.
+ *
+ * We take ownership of "*pLogMessage".
+ */
+void LogWindow::AddLogMessage(LogMessage* pLogMessage)
+{
+    mPool.Add(pLogMessage);
+
+    if (!mPaused && mVisible && FilterMatches(pLogMessage)) {
+        /*
+         * Thought: keep a reference to the previous message.  If it
+         * matches in most fields (all except timestamp?), hold it and
+         * increment a counter.  If we get a message that doesn't match,
+         * or a timer elapses, synthesize a "previous message repeated N
+         * times" string.
+         */
+        AddToDisplay(pLogMessage);
+    }
+
+    // release the initial ref caused by allocation
+    pLogMessage->Release();
+
+    if (mLogFp != NULL)
+        LogToFile(pLogMessage);
+}
+
+/*
+ * Clear out the display, releasing any log messages held in the display
+ * array.
+ */
+void LogWindow::ClearDisplay(void)
+{
+    wxTextCtrl* pTextCtrl;
+    pTextCtrl = (wxTextCtrl*) FindWindow(IDC_LOG_TEXT);
+    pTextCtrl->Clear();
+
+    /*
+     * Just run through the entire array.
+     */
+    for (int i = 0; i < mMaxDisplayMsgs; i++) {
+        if (mDisplayArray[i] != NULL) {
+            mDisplayArray[i]->Release();
+            mDisplayArray[i] = NULL;
+        }
+    }
+    mTopPtr = -1;
+    mNextPtr = 0;
+}
+
+/*
+ * Clear the current display and regenerate it from the log pool.  We need
+ * to do this whenever we change filters or log message formatting.
+ */
+void LogWindow::Redisplay(void)
+{
+    /*
+     * Freeze output rendering so it doesn't flash during update.  Doesn't
+     * seem to help for GTK, and it leaves garbage on the screen in WinXP,
+     * so I'm leaving it commented out.
+     */
+    //wxTextCtrl* pText = (wxTextCtrl*) FindWindow(IDC_LOG_TEXT);
+    //pText->Freeze();
+
+    //printf("--- redisplay\n");
+    ClearDisplay();
+
+    /*
+     * Set up the default wxWidgets text style stuff.
+     */
+    SetTextStyle();
+
+    /*
+     * Here's the plan:
+     * - Start at the head of the pool (where the most recently added
+     *   items are).
+     * - Check to see if the current item passes our filter.  If it does,
+     *   increment the "found count".
+     * - Continue in this manner until we run out of pool or have
+     *   sufficient items to fill the screen.
+     * - Starting from the current position, walk back toward the head,
+     *   adding the items that meet the current filter criteria.
+     *
+     * Don't forget that the log pool could be empty.
+     */
+    LogMessage* pMsg = mPool.GetHead();
+
+    if (pMsg != NULL) {
+        int foundCount = 0;
+
+        // note this stops before it runs off the end
+        while (pMsg->GetNext() != NULL && foundCount < mMaxDisplayMsgs) {
+            if (FilterMatches(pMsg))
+                foundCount++;
+            pMsg = pMsg->GetNext();
+        }
+
+        while (pMsg != NULL) {
+            if (FilterMatches(pMsg))
+                AddToDisplay(pMsg);
+            pMsg = pMsg->GetPrev();
+        }
+    }
+
+    //pText->Thaw();
+}
+
+
+/*
+ * Returns "true" if the currently specified filters would allow this
+ * message to be shown.
+ */
+bool LogWindow::FilterMatches(const LogMessage* pLogMessage)
+{
+    if (pLogMessage->GetPriority() >= mMinPriority)
+        return true;
+    else
+        return false;
+}
+
+/*
+ * Realloc the array of pointers, and remove anything from the display
+ * that should no longer be there.
+ */
+void LogWindow::SetMaxDisplayMsgs(int max)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+
+    pPrefs->SetInt("log-display-msg-count", max);
+}
+
+/*
+ * Add the message to the display array and to the screen.
+ */
+void LogWindow::AddToDisplay(LogMessage* pLogMessage)
+{
+    wxTextCtrl* pTextCtrl;
+    pTextCtrl = (wxTextCtrl*) FindWindow(IDC_LOG_TEXT);
+
+    if (mNextPtr == mTopPtr) {
+        /*
+         * The display array is full.
+         *
+         * We need to eliminate the topmost entry.  This requires removing
+         * it from the array and removing the text from the wxTextCtrl.
+         */
+        pTextCtrl->Remove(0, mDisplayArray[mTopPtr]->GetTextCtrlLen());
+        mDisplayArray[mTopPtr]->Release();
+        mTopPtr = (mTopPtr + 1) % mMaxDisplayMsgs;
+    }
+
+    /*
+     * Add formatted text to the text ctrl.  Track how much actual space
+     * is required.  The space may be different on Win32 (CRLF-based) vs.
+     * GTK (LF-based), so we need to measure it, not compute it from the
+     * text string.
+     */
+    long lastBefore, lastAfter;
+    //long insertBefore;
+    //insertBefore = pTextCtrl->GetInsertionPoint();
+    lastBefore = pTextCtrl->GetLastPosition();
+    FormatMessage(pLogMessage, pTextCtrl);
+    lastAfter = pTextCtrl->GetLastPosition();
+    pLogMessage->SetTextCtrlLen(lastAfter - lastBefore);
+
+    /*
+     * If we restore the old insertion point, we will be glued to where
+     * we were.  This is okay until we start deleting text from the top,
+     * at which point we need to adjust it to retain our position.
+     *
+     * If we set the insertion point to the bottom, we effectively
+     * implement "scroll to bottom on output".
+     *
+     * If we don't set it at all, we get slightly strange behavior out
+     * of GTK, which seems to be par for the course here.
+     */
+    //pTextCtrl->SetInsertionPoint(insertBefore);     // restore insertion pt
+    pTextCtrl->SetInsertionPoint(lastAfter);
+
+    /* add it to array, claim ownership */
+    mDisplayArray[mNextPtr] = pLogMessage;
+    pLogMessage->Acquire();
+
+    /* adjust pointers */
+    if (mTopPtr < 0)        // first time only
+        mTopPtr = 0;
+    mNextPtr = (mNextPtr + 1) % mMaxDisplayMsgs;
+}
+
+
+/*
+ * Return a human-readable string for the priority level.  Always returns
+ * a valid string.
+ */
+static const wxCharBuffer GetPriorityString(android_LogPriority priority)
+{
+    int idx;
+
+    idx = (int) priority - (int) ANDROID_LOG_VERBOSE;
+    if (idx < 0 || idx >= NELEM(gLogLevels))
+        return "?unknown?";
+    return gLogLevels[idx].name.ToAscii();
+}
+
+/*
+ * Format a message and write it to the text control.
+ */
+void LogWindow::FormatMessage(const LogMessage* pLogMessage, 
+    wxTextCtrl* pTextCtrl)
+{
+#if defined(HAVE_LOCALTIME_R)
+    struct tm tmBuf;
+#endif
+    struct tm* ptm;
+    char timeBuf[32];
+    char msgBuf[256];
+    int msgLen = 0;
+    char* outBuf;
+    char priChar;
+    LogPrefsDialog::HeaderFormat headerFmt;
+
+    headerFmt = mHeaderFormat;
+    if (pLogMessage->GetInternal())
+        headerFmt = LogPrefsDialog::kHFInternal;
+
+    priChar = ((const char*)GetPriorityString(pLogMessage->GetPriority()))[0];
+
+    /*
+     * Get the current date/time in pretty form
+     *
+     * It's often useful when examining a log with "less" to jump to
+     * a specific point in the file by searching for the date/time stamp.
+     * For this reason it's very annoying to have regexp meta characters
+     * in the time stamp.  Don't use forward slashes, parenthesis,
+     * brackets, asterisks, or other special chars here.
+     */
+    time_t when = pLogMessage->GetWhen();
+    const char* fmt = NULL;
+#if defined(HAVE_LOCALTIME_R)
+    ptm = localtime_r(&when, &tmBuf);
+#else
+    ptm = localtime(&when);
+#endif
+    switch (headerFmt) {
+    case LogPrefsDialog::kHFFull:
+    case LogPrefsDialog::kHFInternal:
+        fmt = "%m-%d %H:%M:%S";
+        break;
+    case LogPrefsDialog::kHFBrief:
+    case LogPrefsDialog::kHFMinimal:
+        fmt = "%H:%M:%S";
+        break;
+    default:
+        break;
+    }
+    if (fmt != NULL)
+        strftime(timeBuf, sizeof(timeBuf), fmt, ptm);
+    else
+        strcpy(timeBuf, "-");
+
+    const int kMaxExtraNewlines = 2;
+    char hdrNewline[2];
+    char finalNewlines[kMaxExtraNewlines+1 +1];
+
+    if (mSingleLine)
+        hdrNewline[0] = ' ';
+    else
+        hdrNewline[0] = '\n';
+    hdrNewline[1] = '\0';
+
+    assert(mExtraSpacing <= kMaxExtraNewlines);
+    int i;
+    for (i = 0; i < mExtraSpacing+1; i++)
+        finalNewlines[i] = '\n';
+    finalNewlines[i] = '\0';
+
+    wxTextAttr msgColor;
+    switch (pLogMessage->GetPriority()) {
+    case ANDROID_LOG_WARN:
+        msgColor.SetTextColour(*wxBLUE);
+        break;
+    case ANDROID_LOG_ERROR:
+        msgColor.SetTextColour(*wxRED);
+        break;
+    case ANDROID_LOG_VERBOSE:
+    case ANDROID_LOG_DEBUG:
+    case ANDROID_LOG_INFO:
+    default:
+        msgColor.SetTextColour(*wxBLACK);
+        break;
+    }
+    if (pLogMessage->GetInternal())
+        msgColor.SetTextColour(*wxGREEN);
+
+    /*
+     * Construct a buffer containing the log header.
+     */
+    bool splitHeader = true;
+    outBuf = msgBuf;
+    switch (headerFmt) {
+    case LogPrefsDialog::kHFFull:
+        splitHeader = true;
+        msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf),
+                    "[ %s %5d %c/%-6.6s]%s",
+                    timeBuf, pLogMessage->GetPid(), priChar,
+                    pLogMessage->GetTag(), hdrNewline);
+        break;
+    case LogPrefsDialog::kHFBrief:
+        splitHeader = true;
+        msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf),
+                    "[%s %5d]%s",
+                    timeBuf, pLogMessage->GetPid(), hdrNewline);
+        break;
+    case LogPrefsDialog::kHFMinimal:
+        splitHeader = false;
+        msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf),
+                    "%s %5d- %s",
+                    timeBuf, pLogMessage->GetPid(), pLogMessage->GetMsg());
+        break;
+    case LogPrefsDialog::kHFInternal:
+        splitHeader = false;
+        msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf),
+                    "[%s] %s", timeBuf, pLogMessage->GetMsg());
+        break;
+    default:
+        fprintf(stderr, "Sim: unexpected header format %d\n", headerFmt);
+        assert(false);
+        break;
+    }
+
+    if (msgLen < 0) {
+        fprintf(stderr, "WHOOPS\n");
+        assert(outBuf == msgBuf);
+        return;
+    }
+
+    if (splitHeader) {
+        if (mUseColor)
+            pTextCtrl->SetDefaultStyle(wxTextAttr(*wxLIGHT_GREY));
+        pTextCtrl->AppendText(wxString::FromAscii(outBuf));
+        if (mUseColor)
+            pTextCtrl->SetDefaultStyle(msgColor);
+        pTextCtrl->AppendText(wxString::FromAscii(pLogMessage->GetMsg()));
+        if (mUseColor)
+            pTextCtrl->SetDefaultStyle(*wxBLACK);
+        pTextCtrl->AppendText(wxString::FromAscii(finalNewlines));
+    } else {
+        if (mUseColor)
+            pTextCtrl->SetDefaultStyle(msgColor);
+        pTextCtrl->AppendText(wxString::FromAscii(outBuf));
+        if (mUseColor)
+            pTextCtrl->SetDefaultStyle(*wxBLACK);
+        pTextCtrl->AppendText(wxString::FromAscii(finalNewlines));
+    }
+
+    /* if we allocated storage for this message, free it */
+    if (outBuf != msgBuf)
+        free(outBuf);
+}
+
+/*
+ * Write the message to the log file.
+ *
+ * We can't just do this in FormatMessage(), because that re-writes all
+ * messages on the display whenever the output format or filter changes.
+ *
+ * Use a one-log-per-line format here to make "grep" useful.
+ */
+void LogWindow::LogToFile(const LogMessage* pLogMessage)
+{
+#if defined(HAVE_LOCALTIME_R)
+    struct tm tmBuf;
+#endif
+    struct tm* ptm;
+    char timeBuf[32];
+    char msgBuf[256];
+    int msgLen;
+    char* outBuf;
+    char priChar;
+
+    assert(mLogFp != NULL);
+
+    time_t when = pLogMessage->GetWhen();
+#if defined(HAVE_LOCALTIME_R)
+    ptm = localtime_r(&when, &tmBuf);
+#else
+    ptm = localtime(&when);
+#endif
+
+    strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+    priChar = ((const char*)GetPriorityString(pLogMessage->GetPriority()))[0];
+
+    outBuf = msgBuf;
+    if (pLogMessage->GetInternal()) {
+        msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf),
+                    "[%s %5d *] %s\n",
+                    timeBuf, pLogMessage->GetPid(), pLogMessage->GetMsg());
+    } else {
+        msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf),
+                    "[%s %5d %c] %s)\n",
+                    timeBuf, pLogMessage->GetPid(), priChar,
+                    pLogMessage->GetMsg());
+    }
+    if (fwrite(outBuf, msgLen, 1, mLogFp) != 1)
+        fprintf(stderr, "Sim: WARNING: partial log write\n");
+    fflush(mLogFp);
+
+    /* if we allocated storage for this message, free it */
+    if (outBuf != msgBuf)
+        free(outBuf);
+}
+
+/*
+ * Get the modification date of a file.
+ */
+static bool GetFileModDate(const char* fileName, time_t* pModWhen)
+{
+    struct stat sb;
+
+    if (stat(fileName, &sb) < 0)
+        return false;
+
+    *pModWhen = sb.st_mtime;
+    return true;
+}
+
+/*
+ * Open or close the log file as appropriate.
+ */
+void LogWindow::PrepareLogFile(void)
+{
+    const int kLogFileMaxAge = 8 * 60 * 60;     // 8 hours
+
+    if (!mWriteFile && mLogFp != NULL) {
+        printf("Sim: closing log file\n");
+        fclose(mLogFp);
+        mLogFp = NULL;
+    } else if (mWriteFile && mLogFp == NULL) {
+        printf("Sim: opening log file '%s'\n", (const char*)mFileName.ToAscii());
+        time_t now, modWhen = 0;
+        const char* openFlags;
+
+        now = time(NULL);
+        if (!mTruncateOld ||
+            (GetFileModDate(mFileName.ToAscii(), &modWhen) &&
+             modWhen + kLogFileMaxAge > now))
+        {
+            if (modWhen != 0) {
+                printf("--- log file is %.3f hours old, appending\n",
+                    (now - modWhen) / 3600.0);
+            }
+            openFlags = "a";        // open for append (text mode)
+        } else {
+            if (modWhen != 0) {
+                printf("--- log file is %.3f hours old, truncating\n",
+                    (now - modWhen) / 3600.0);
+            }
+            openFlags = "w";        // open for writing, truncate (text mode)
+        }
+
+        mLogFp = fopen(mFileName.ToAscii(), openFlags);
+        if (mLogFp == NULL) {
+            fprintf(stderr, "Sim: failed opening log file '%s': %s\n",
+                (const char*) mFileName.ToAscii(), strerror(errno));
+        } else {
+            fprintf(mLogFp, "\n\n");
+            fflush(mLogFp);
+        }
+    }
+}
+
+/*
+ * Add a new log message.
+ *
+ * This function can be called from any thread.  It makes a copy of the
+ * stuff in "*pBundle" and sends it to the main UI thread.
+ */
+/*static*/ void LogWindow::PostLogMsg(const android_LogBundle* pBundle)
+{
+    LogMessage* pNewMessage = LogMessage::Create(pBundle);
+
+    SendToWindow(pNewMessage);
+}
+
+/*
+ * Post a simple string to the log.
+ */
+/*static*/ void LogWindow::PostLogMsg(const char* msg)
+{
+    LogMessage* pNewMessage = LogMessage::Create(msg);
+
+    SendToWindow(pNewMessage);
+}
+
+/*
+ * Post a simple wxString to the log.
+ */
+/*static*/ void LogWindow::PostLogMsg(const wxString& msg)
+{
+    LogMessage* pNewMessage = LogMessage::Create(msg.ToAscii());
+
+    SendToWindow(pNewMessage);
+}
+
+/*
+ * Send a log message to the log window.
+ */
+/*static*/ void LogWindow::SendToWindow(LogMessage* pMessage)
+{
+    if (pMessage != NULL) {
+        wxWindow* pMainFrame = ((MyApp*)wxTheApp)->GetMainFrame();
+        UserEventMessage* pUem = new UserEventMessage;
+        pUem->CreateLogMessage(pMessage);
+
+        UserEvent uev(0, (void*) pUem);
+
+        pMainFrame->AddPendingEvent(uev);
+    } else {
+        fprintf(stderr, "Sim: failed to add new log message\n");
+    }
+}
+
+
+/*
+ * This is a sanity check.  We need to stop somewhere to avoid trashing
+ * the system on bad input.
+ */
+#define kMaxLen 65536
+
+#define VSNPRINTF vsnprintf     // used to worry about _vsnprintf
+
+
+/*
+ * Print a formatted message into a buffer.  Pass in a buffer to try to use.
+ *
+ * If the buffer isn't big enough to hold the message, allocate storage
+ * with malloc() and return that instead.  The caller is responsible for
+ * freeing the storage.
+ *
+ * Returns the length of the string, or -1 if the printf call failed.
+ */
+static int android_vsnprintfBuffer(char** pBuf, int bufLen, const char* format, va_list args)
+{
+    int charsOut;
+    char* localBuf = NULL;
+
+    assert(pBuf != NULL && *pBuf != NULL);
+    assert(bufLen > 0);
+    assert(format != NULL);
+
+    while (1) {
+        /*
+         * In some versions of libc, vsnprintf only returns 0 or -1, where
+         * -1 indicates the the buffer wasn't big enough.  In glibc 2.1
+         * and later, it returns the actual size needed.
+         *
+         * MinGW is just returning -1, so we have to retry there.
+         */
+        char* newBuf;
+
+        charsOut = VSNPRINTF(*pBuf, bufLen, format, args);
+
+        if (charsOut >= 0 && charsOut < bufLen)
+            break;
+
+        //fprintf(stderr, "EXCEED: %d vs %d\n", charsOut, bufLen);
+        if (charsOut < 0) {
+            /* exact size not known, double previous size */
+            bufLen *= 2;
+            if (bufLen > kMaxLen)
+                goto fail;
+        } else {
+            /* exact size known, just use that */
+
+            bufLen = charsOut + 1;
+        }
+        //fprintf(stderr, "RETRY at %d\n", bufLen);
+
+        newBuf = (char*) realloc(localBuf, bufLen);
+        if (newBuf == NULL)
+            goto fail;
+        *pBuf = localBuf = newBuf;
+    }
+
+    // On platforms where snprintf() doesn't return the number of
+    // characters output, we would need to call strlen() here.
+
+    return charsOut;
+
+fail:
+    if (localBuf != NULL) {
+        free(localBuf);
+        *pBuf = NULL;
+    }
+    return -1;
+}
+
+/*
+ * Variable-arg form of the above.
+ */
+static int android_snprintfBuffer(char** pBuf, int bufLen, const char* format, ...)
+{
+    va_list args;
+    int result;
+
+    va_start(args, format);
+    result = android_vsnprintfBuffer(pBuf, bufLen, format, args);
+    va_end(args);
+
+    return result;
+}
+
+
diff --git a/simulator/app/LogWindow.h b/simulator/app/LogWindow.h
new file mode 100644
index 0000000..ca9f133
--- /dev/null
+++ b/simulator/app/LogWindow.h
@@ -0,0 +1,127 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Window with log output.
+//
+#ifndef _SIM_LOG_WINDOW_H
+#define _SIM_LOG_WINDOW_H
+
+#include "PhoneData.h"
+#include "UserEvent.h"
+#include "LogMessage.h"
+#include "LogPool.h"
+#include "LogPrefsDialog.h"
+
+
+/*
+ * Display log output from runtime process.
+ *
+ * We receive the messages broken into components (date, log level, tag,
+ * function name, etc.) and do the formatting ourselves.  We receive all
+ * messages regardless of log level, and provide filter controls in the
+ * window.
+ *
+ * Messages are stored in a "log pool", which has a fixed memory footprint.
+ * The messages that are currently visible in the log output window are
+ * also pointed at from a fixed-size display array.  Changes to output
+ * format cause us to clear the display and re-show everything in the
+ * display array, while changes to the output filter cause us to
+ * re-evaluate the contents of the display pool.
+ */
+class LogWindow : public wxDialog {
+public:
+    LogWindow(wxWindow* parent);
+    virtual ~LogWindow(void);
+
+    /* we override this, to cope with annoying GTK behavior */
+    virtual bool Show(bool show = true);
+
+    /* return preferred size and position */
+    static wxRect GetPrefWindowRect(void);
+
+    /* handle a log message "user event" */
+    void AddLogMessage(LogMessage* pLogMessage);
+
+    /* resize the display messages array */
+    void SetMaxDisplayMsgs(int max);
+
+    /* post a message to the log; may be called from non-main thread */
+    static void PostLogMsg(const android_LogBundle* pBundle);
+    static void PostLogMsg(const wxString& msg);
+    static void PostLogMsg(const char* msg);
+
+private:
+    void OnMove(wxMoveEvent& event);
+    void OnClose(wxCloseEvent& event);
+    void OnLogLevel(wxCommandEvent& event);
+    void OnLogClear(wxCommandEvent& event);
+    void OnLogPause(wxCommandEvent& event);
+    void OnLogPrefs(wxCommandEvent& event);
+
+    /* handle incoming log message */
+    void OnUserEvent(UserEvent& event);
+
+    void SaveWindowPrefs(void);
+    void ConstructControls(void);
+
+    void AddToDisplay(LogMessage* pLogMessage);
+    void ClearDisplay(void);
+    void Redisplay(void);
+    void SetTextStyle(void);
+
+    bool FilterMatches(const LogMessage* pLogMessage);
+
+    void FormatMessage(const LogMessage* pLogMessage, 
+        wxTextCtrl* pTextCtrl);
+
+    void LogToFile(const LogMessage* pLogMessage);
+    void PrepareLogFile(void);
+    static void SendToWindow(LogMessage* pMessage);
+
+    /*
+     * Message pool.
+     */
+    LogPool     mPool;
+
+    /*
+     * Display array.  This is a fixed-size circular array that holds
+     * pointers to the log messages currently displayed on screen.
+     */
+    LogMessage**    mDisplayArray;      // ptrs to messages currently displayed
+    int         mMaxDisplayMsgs;        // max #of messages
+    int         mTopPtr;                // index of top message
+    int         mNextPtr;               // index of next empty slot
+
+    bool        mPaused;                // is output paused for review?
+
+    /*
+     * Current filter.
+     */
+    android_LogPriority mMinPriority;   // messages at or above are shown
+
+    /* format options */
+    LogPrefsDialog::HeaderFormat mHeaderFormat;
+    bool        mSingleLine;            // put whole message on one line?
+    int         mExtraSpacing;          // double/triple-space messages?
+    int         mPointSize;             // text point size;
+    bool        mUseColor;              // colorful messages?
+    bool        mFontMonospace;         // use monospace font?
+
+    /* log file options */
+    bool        mWriteFile;
+    wxString    mFileName;
+    bool        mTruncateOld;
+
+    FILE*       mLogFp;
+
+    /*
+     * Window position stuff.
+     */
+    bool        mNewlyShown;
+    wxPoint     mLastPosition;
+    bool        mVisible;
+
+    DECLARE_EVENT_TABLE()
+};
+
+#endif // _SIM_LOG_WINDOW_H
diff --git a/simulator/app/MainFrame.cpp b/simulator/app/MainFrame.cpp
new file mode 100644
index 0000000..b13caab
--- /dev/null
+++ b/simulator/app/MainFrame.cpp
@@ -0,0 +1,1462 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Main window, menu bar, and associated goodies.
+//
+
+// 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/button.h"
+#include "wx/help.h"
+#include "wx/filedlg.h"
+#include "wx/slider.h"
+#include "wx/textctrl.h"
+
+#include "MainFrame.h"
+#include "MyApp.h"
+#include "Resource.h"
+#include "PhoneCollection.h"
+#include "PhoneData.h"
+#include "PhoneWindow.h"
+#include "DeviceWindow.h"
+#include "UserEventMessage.h"
+#include "PrefsDialog.h"
+
+#include "SimRuntime.h"
+
+
+static wxString kStatusNotRunning = wxT("Idle");
+static wxString kStatusRunning = wxT("Run");
+
+static wxString kDeviceMenuString = wxT("&Device");
+
+static const wxString gStdJavaApps[] = {
+    wxT(""),
+    wxT("com.android.testharness.TestList"),
+    wxT("com.android.apps.contacts.ContactsList"),
+    wxT("mikeapp")
+};
+
+
+BEGIN_EVENT_TABLE(MainFrame::MainFrame, wxFrame)
+    EVT_CLOSE(MainFrame::OnClose)
+    EVT_TIMER(kHalfSecondTimerId, MainFrame::OnTimer)
+    //EVT_IDLE(MainFrame::OnIdle)
+  
+    EVT_ACTIVATE(MainFrame::OnActivate)
+    EVT_ACTIVATE_APP(MainFrame::OnActivate)
+    EVT_COMBOBOX(IDC_MODE_SELECT, MainFrame::OnComboBox)
+    EVT_COMBOBOX(IDC_JAVA_VM, MainFrame::OnComboBox)
+    EVT_CHECKBOX(IDC_USE_GDB, MainFrame::OnCheckBox)
+    EVT_CHECKBOX(IDC_USE_VALGRIND, MainFrame::OnCheckBox)
+    EVT_CHECKBOX(IDC_CHECK_JNI, MainFrame::OnCheckBox)
+    EVT_CHECKBOX(IDC_OVERLAY_ONION_SKIN, MainFrame::OnCheckBox)
+    EVT_TEXT(IDC_JAVA_APP_NAME, MainFrame::OnText)
+    EVT_TEXT_ENTER(IDC_ONION_SKIN_FILE_NAME, MainFrame::OnTextEnter)
+    EVT_BUTTON(IDC_ONION_SKIN_BUTTON, MainFrame::OnButton)
+    EVT_COMMAND_SCROLL(IDC_ONION_SKIN_ALPHA_VAL, MainFrame::OnSliderChange)
+
+    EVT_MENU(IDM_FILE_PREFERENCES, MainFrame::OnFilePreferences)
+    EVT_MENU(IDM_FILE_EXIT, MainFrame::OnFileExit)
+    EVT_MENU(IDM_RUNTIME_START, MainFrame::OnSimStart)
+    EVT_UPDATE_UI(IDM_RUNTIME_START, MainFrame::OnUpdateSimStart)
+    EVT_MENU(IDM_RUNTIME_STOP, MainFrame::OnSimStop)
+    EVT_UPDATE_UI(IDM_RUNTIME_STOP, MainFrame::OnUpdateSimStop)
+    EVT_MENU(IDM_RUNTIME_RESTART, MainFrame::OnSimRestart)
+    EVT_UPDATE_UI(IDM_RUNTIME_RESTART, MainFrame::OnUpdateSimRestart)
+    EVT_MENU(IDM_RUNTIME_KILL, MainFrame::OnSimKill)
+    EVT_UPDATE_UI(IDM_RUNTIME_KILL, MainFrame::OnUpdateSimKill)
+    EVT_MENU_RANGE(IDM_DEVICE_SEL0, IDM_DEVICE_SELN,
+        MainFrame::OnDeviceSelected)
+    EVT_MENU(IDM_DEVICE_RESCAN, MainFrame::OnDeviceRescan)
+    EVT_UPDATE_UI(IDM_DEBUG_SHOW_LOG, MainFrame::OnUpdateDebugShowLog)
+    EVT_MENU(IDM_DEBUG_SHOW_LOG, MainFrame::OnDebugShowLog)
+    EVT_MENU(IDM_HELP_CONTENTS, MainFrame::OnHelpContents)
+    EVT_MENU(IDM_HELP_ABOUT, MainFrame::OnHelpAbout)
+
+    EVT_USER_EVENT(MainFrame::OnUserEvent)
+END_EVENT_TABLE()
+
+
+/*
+ * Main window constructor.
+ *
+ * Creates menus and status bar.
+ */
+MainFrame::MainFrame(const wxString& title, const wxPoint& pos,
+    const wxSize& size, long style)
+    : wxFrame((wxFrame *)NULL, -1, title, pos, size, style),
+      mSimRunning(false),
+      mRestartRequested(false),
+      mpPhoneWindow(NULL),
+      mPhoneWindowPosn(wxDefaultPosition),
+      mTimer(this, kHalfSecondTimerId)
+{
+    mSimAssetPath = ((MyApp*)wxTheApp)->GetSimAssetPath();
+    mSimAssetPath += wxT("/simulator/default/default");
+
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    int val;
+
+    val = mPhoneWindowPosn.x;
+    pPrefs->GetInt("window-device-x", &val);
+    mPhoneWindowPosn.x = val;
+    val = mPhoneWindowPosn.y;
+    pPrefs->GetInt("window-device-y", &val);
+    mPhoneWindowPosn.y = val;
+
+    /*
+     * Create main menu.
+     */
+    ConstructMenu();
+
+    /*
+     * Create the status bar.
+     */
+    int widths[2] = { -1, 50 };
+    CreateStatusBar(2, wxFULL_REPAINT_ON_RESIZE);   // no wxST_SIZEGRIP
+    SetStatusWidths(2, widths);
+    SetStatusText(wxT("Ready"));
+    SetStatusText(kStatusNotRunning, 1);
+
+    /*
+     * Create main window controls.
+     */
+    ConstructControls();
+
+#if 0
+    /*
+     * Use the standard window color for the main frame (which usually
+     * has a darker color).  This has a dramatic effect under Windows.
+     */
+    wxColour color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
+    SetOwnBackgroundColour(color);
+#endif
+
+    /*
+     * Create the log window.
+     */
+    wxRect layout = LogWindow::GetPrefWindowRect();
+    mpLogWindow = new LogWindow(this);
+    mpLogWindow->Move(layout.GetTopLeft());
+    mpLogWindow->SetSize(layout.GetSize());
+    bool showLogWindow = true;
+    pPrefs->GetBool("window-log-show", &showLogWindow);
+    if (showLogWindow)
+        mpLogWindow->Show();
+
+    /*
+     * Set up a frequent timer.  We use this to keep our "run/idle"
+     * display up to date.  (Ideally this will go away.)
+     */
+    mTimer.Start(400);      // arg is delay in ms
+
+    /*
+     * Handle auto-power-on by sending ourselves an event.  That way it
+     * gets handled after window initialization finishes.
+     */
+    bool autoPowerOn = false;
+    pPrefs->GetBool("auto-power-on", &autoPowerOn);
+    if (autoPowerOn) {
+        printf("Sim: Auto power-up\n");
+        wxCommandEvent startEvent(wxEVT_COMMAND_MENU_SELECTED, IDM_RUNTIME_START);
+        this->AddPendingEvent(startEvent);
+    }
+
+    /*
+     * wxThread wants these to be on the heap -- it will call delete on the
+     * object when the thread exits.
+     */
+    mExternalRuntimeThread = new ExternalRuntime();
+    mExternalRuntimeThread->StartThread();
+    mPropertyServerThread = new PropertyServer();
+    mPropertyServerThread->StartThread();
+}
+
+/*
+ * Construct the main menu.  Called from the constructor.
+ */
+void MainFrame::ConstructMenu(void)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+
+    /*
+     * Scan for available phones.
+     */
+    PhoneCollection* pCollection = PhoneCollection::GetInstance();
+    pCollection->ScanForPhones(mSimAssetPath.ToAscii());
+
+    /*
+     * Create the "File" menu.
+     */
+    wxMenu* menuFile = new wxMenu;
+
+    menuFile->Append(IDM_FILE_PREFERENCES, wxT("&Preferences..."),
+        wxT("Edit simulator preferences"));
+    menuFile->AppendSeparator();
+    menuFile->Append(IDM_FILE_EXIT, wxT("E&xit\tCtrl-Q"),
+        wxT("Stop simulator and exit"));
+
+    /*
+     * Create the "Runtime" menu.
+     */
+    wxMenu* menuRuntime = new wxMenu;
+    menuRuntime->Append(IDM_RUNTIME_START, wxT("&Power On\tCtrl-G"),
+        wxT("Start the device"));
+//    menuRuntime->Append(IDM_RUNTIME_STOP, wxT("Power &Off"),
+//        wxT("Stop the device"));
+    menuRuntime->AppendSeparator();
+//    menuRuntime->Append(IDM_RUNTIME_RESTART, wxT("&Restart"),
+//        wxT("Restart the device"));
+    menuRuntime->Append(IDM_RUNTIME_KILL, wxT("&Kill\tCtrl-K"),
+        wxT("Kill the runtime processes"));
+
+    /*
+     * Create "Device" menu.
+     */
+    wxString defaultDevice = wxT("Sooner");
+    pPrefs->GetString("default-device", /*ref*/ defaultDevice);
+    wxMenu* menuDevice = CreateDeviceMenu(defaultDevice.ToAscii());
+
+    /*
+     * Create "Debug" menu.
+     */
+    wxMenu* menuDebug = new wxMenu;
+    menuDebug->AppendCheckItem(IDM_DEBUG_SHOW_LOG, wxT("View &Log Output"),
+        wxT("View log output window"));
+
+    /*
+     * Create the "Help" menu.
+     */
+    wxMenu* menuHelp = new wxMenu;
+    menuHelp->Append(IDM_HELP_CONTENTS, wxT("&Contents...\tF1"),
+        wxT("Simulator help"));
+    menuHelp->AppendSeparator();
+    menuHelp->Append(IDM_HELP_ABOUT, wxT("&About..."),
+        wxT("See the fabulous 'about' box"));
+
+    /*
+     * Create the menu bar.
+     */
+    wxMenuBar *menuBar = new wxMenuBar;
+    menuBar->Append(menuFile, wxT("&File"));
+    menuBar->Append(menuDevice, kDeviceMenuString);
+    menuBar->Append(menuRuntime, wxT("&Runtime"));
+    menuBar->Append(menuDebug, wxT("&Debug"));
+    menuBar->Append(menuHelp, wxT("&Help"));
+
+    SetMenuBar(menuBar);
+
+}
+
+/*
+ * Construct the "device" menu from our phone collection.
+ */
+wxMenu* MainFrame::CreateDeviceMenu(const char* defaultItemName)
+{
+    wxMenu* menuDevice = new wxMenu;
+    PhoneCollection* pCollection = PhoneCollection::GetInstance();
+    int defaultModel = 0;
+
+    for (int i = 0; i < pCollection->GetPhoneCount(); i++) {
+        PhoneData* pPhoneData = pCollection->GetPhoneData(i);
+        assert(pPhoneData != NULL);
+
+        menuDevice->AppendRadioItem(IDM_DEVICE_SEL0 + i,
+            wxString::FromAscii(pPhoneData->GetTitle()));
+
+        // use this one as default if the string matches
+        if (strcasecmp(pPhoneData->GetName(), defaultItemName) == 0)
+            defaultModel = i;
+    }
+
+    menuDevice->Check(IDM_DEVICE_SEL0 + defaultModel, true);
+
+    menuDevice->AppendSeparator();
+    menuDevice->Append(IDM_DEVICE_RESCAN, wxT("Re-scan"));
+
+    return menuDevice;
+}
+
+/*
+ * Create some controls in the main window.
+ *
+ * The main frame doesn't use the normal background color that you find
+ * in dialog windows, so we create a "panel" and put all the controls
+ * on that.
+ */
+void MainFrame::ConstructControls(void)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    wxPanel* base = new wxPanel(this, wxID_ANY);
+    wxBoxSizer* masterSizer = new wxBoxSizer(wxVERTICAL);
+    wxBoxSizer* tmpSizer;
+    wxStaticBoxSizer* displayOptSizer;
+    wxStaticBoxSizer* runtimeOptSizer;
+    wxStaticBoxSizer* onionSkinOptSizer;
+    wxComboBox* pModeSelection;
+    wxCheckBox* pUseGDB;
+    wxCheckBox* pUseValgrind;
+    wxCheckBox* pCheckJni;
+    wxCheckBox* pOverlayOnionSkin;
+    
+    displayOptSizer = new wxStaticBoxSizer(wxHORIZONTAL, base,
+        wxT("Configuration"));
+    runtimeOptSizer = new wxStaticBoxSizer(wxVERTICAL, base,
+        wxT("Runtime Options"));
+    onionSkinOptSizer = new wxStaticBoxSizer(wxVERTICAL, base,
+        wxT("Onion Skin Options"));
+
+    /*
+     * Set up the configuration sizer (nee "display options").
+     */
+    tmpSizer = new wxBoxSizer(wxHORIZONTAL);
+    displayOptSizer->Add(tmpSizer);
+    tmpSizer->Add(
+            new wxStaticText(base, wxID_ANY, wxT("Device mode:"),
+            wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT), 0, wxALIGN_CENTER_VERTICAL);
+    pModeSelection = new wxComboBox(base, IDC_MODE_SELECT, wxT(""),
+            wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY);
+    tmpSizer->AddSpacer(kInterSpacing);
+    tmpSizer->Add(pModeSelection);
+
+    displayOptSizer->AddSpacer(kInterSpacing);
+
+    /*
+     * Configure the runtime options sizer.
+     */
+    wxComboBox* pJavaAppName;
+    tmpSizer = new wxBoxSizer(wxHORIZONTAL);
+    pUseGDB = new wxCheckBox(base, IDC_USE_GDB, wxT("Use &debugger"));
+    tmpSizer->Add(pUseGDB);
+    tmpSizer->AddSpacer(kInterSpacing);
+    pUseValgrind = new wxCheckBox(base, IDC_USE_VALGRIND, wxT("Use &valgrind"));
+    tmpSizer->Add(pUseValgrind);
+    tmpSizer->AddSpacer(kInterSpacing);
+    pCheckJni = new wxCheckBox(base, IDC_CHECK_JNI, wxT("Check &JNI"));
+    tmpSizer->Add(pCheckJni);
+
+    pJavaAppName = new wxComboBox(base, IDC_JAVA_APP_NAME, wxT(""),
+        wxDefaultPosition, wxSize(320, -1), NELEM(gStdJavaApps), gStdJavaApps,
+        wxCB_DROPDOWN);
+    wxBoxSizer* javaAppSizer = new wxBoxSizer(wxHORIZONTAL);
+    javaAppSizer->Add(
+            new wxStaticText(base, wxID_ANY,
+                wxT("Java app:"),
+                wxDefaultPosition, wxDefaultSize,
+                wxALIGN_LEFT),
+            0, wxALIGN_CENTER_VERTICAL);
+    javaAppSizer->AddSpacer(kInterSpacing);
+    javaAppSizer->Add(pJavaAppName);
+
+    runtimeOptSizer->Add(tmpSizer);
+
+    runtimeOptSizer->AddSpacer(kInterSpacing);
+    runtimeOptSizer->Add(javaAppSizer);
+    runtimeOptSizer->AddSpacer(kInterSpacing);
+
+    wxString tmpStr;
+    SetCheckFromPref(pUseGDB, "debug", false);
+    SetCheckFromPref(pUseValgrind, "valgrind", false);
+    SetCheckFromPref(pCheckJni, "check-jni", false);
+    if (pPrefs->GetString("java-app-name", /*ref*/ tmpStr))
+        pJavaAppName->SetValue(tmpStr);
+
+    /*
+     * Configure the onion skin options sizer.
+     */
+    wxTextCtrl* pOnionSkinFileNameText;
+    wxButton* pOnionSkinFileButton;
+    wxSlider* pOnionSkinAlphaSlider;
+    tmpSizer = new wxBoxSizer(wxHORIZONTAL);
+    pOverlayOnionSkin = new wxCheckBox(base, 
+        IDC_OVERLAY_ONION_SKIN, wxT("Overlay &onion skin"));
+    tmpSizer->Add(pOverlayOnionSkin);
+
+    pOnionSkinFileNameText = new wxTextCtrl(base, 
+        IDC_ONION_SKIN_FILE_NAME, wxT(""),
+        wxDefaultPosition, wxSize(250, -1),
+        wxTE_PROCESS_ENTER);
+    pOnionSkinFileButton = new wxButton(base, IDC_ONION_SKIN_BUTTON,
+        wxT("Choose"));
+
+    wxBoxSizer* onionSkinFileNameSizer = new wxBoxSizer(wxHORIZONTAL);
+    onionSkinFileNameSizer->Add(
+        new wxStaticText(base, wxID_ANY,
+            wxT("Filename:"),
+            wxDefaultPosition, wxDefaultSize,
+            wxALIGN_LEFT),
+        0, wxALIGN_CENTER_VERTICAL);
+    onionSkinFileNameSizer->AddSpacer(kInterSpacing);
+    onionSkinFileNameSizer->Add(pOnionSkinFileNameText);
+    onionSkinFileNameSizer->Add(pOnionSkinFileButton);
+
+    wxBoxSizer * onionSkinAlphaSizer = new wxBoxSizer(wxHORIZONTAL);
+    int initialAlphaVal = 127;
+    pPrefs->GetInt("onion-skin-alpha-value", &initialAlphaVal);
+    pOnionSkinAlphaSlider = new wxSlider(base, IDC_ONION_SKIN_ALPHA_VAL,
+        initialAlphaVal, 0, 255, wxDefaultPosition, wxSize(150, 20));
+    onionSkinAlphaSizer->Add(
+        new wxStaticText(base, wxID_ANY,
+            wxT("Transparency:"),
+            wxDefaultPosition, wxDefaultSize,
+            wxALIGN_LEFT),
+        0, wxALIGN_CENTER_VERTICAL);
+    onionSkinAlphaSizer->AddSpacer(kInterSpacing);
+    onionSkinAlphaSizer->Add(pOnionSkinAlphaSlider, 1, wxCENTRE | wxALL, 5);
+
+    onionSkinOptSizer->Add(tmpSizer);
+    onionSkinOptSizer->AddSpacer(kInterSpacing);
+    onionSkinOptSizer->Add(onionSkinFileNameSizer);
+    onionSkinOptSizer->Add(onionSkinAlphaSizer);
+
+    wxString tmpStr2;
+    SetCheckFromPref(pOverlayOnionSkin, "overlay-onion-skin", false);
+    if (pPrefs->GetString("onion-skin-file-name", /*ref*/ tmpStr2))
+        pOnionSkinFileNameText->SetValue(tmpStr2);
+
+    /*
+     * Add the various components to the master sizer.
+     */
+    masterSizer->Add(displayOptSizer);
+    masterSizer->AddSpacer(kInterSpacing * 2);
+    masterSizer->Add(runtimeOptSizer);
+    masterSizer->AddSpacer(kInterSpacing * 2);
+    masterSizer->Add(onionSkinOptSizer);
+    //masterSizer->AddSpacer(kInterSpacing);
+
+    /*
+     * I don't see a way to guarantee that the window is wide enough to
+     * show the entire menu bar, so just throw some pixels at it.
+     */
+    wxBoxSizer* minWidthSizer = new wxBoxSizer(wxVERTICAL);
+    minWidthSizer->Add(300, kEdgeSpacing);       // forces minimum width
+    minWidthSizer->Add(masterSizer);
+    minWidthSizer->AddSpacer(kInterSpacing * 2);
+
+    /* move us a few pixels in from the left */
+    wxBoxSizer* indentSizer = new wxBoxSizer(wxHORIZONTAL);
+    indentSizer->AddSpacer(kEdgeSpacing);
+    indentSizer->Add(minWidthSizer);
+    indentSizer->AddSpacer(kEdgeSpacing);
+
+    base->SetSizer(indentSizer);
+
+    indentSizer->Fit(this);
+    indentSizer->SetSizeHints(this);
+}
+
+/*
+ * Set the value of a checkbox based on a value from the config file.
+ */
+void MainFrame::SetCheckFromPref(wxCheckBox* pControl, const char* prefStr,
+    bool defaultVal)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    bool val = defaultVal;
+    pPrefs->GetBool(prefStr, &val);
+
+    pControl->SetValue(val);
+}
+
+/*
+ * Destructor.
+ */
+MainFrame::~MainFrame(void)
+{
+    PhoneCollection::DestroyInstance();
+
+    delete mExternalRuntimeThread;
+    delete mPropertyServerThread;
+
+    // don't touch mpModeSelection -- child of window
+}
+
+/*
+ * File->Quit or click on close box.
+ *
+ * If we want an "are you sure you want to quit" box, add it here.
+ */
+void MainFrame::OnClose(wxCloseEvent& event)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+
+/*
+    if (event.CanVeto())
+        printf("Closing (can veto)\n");
+    else
+        printf("Closing (mandatory)\n");
+*/
+
+    /*
+     * Generally speaking, Close() is not guaranteed to close the window.
+     * However, we want to use it here because (a) our windows are
+     * guaranteed to close, and (b) it provides our windows an opportunity
+     * to tell others that they are about to vanish.
+     */
+    if (mpPhoneWindow != NULL)
+        mpPhoneWindow->Close(true);
+
+    /* save position of main window */
+    wxPoint pos = GetPosition();
+    pPrefs->SetInt("window-main-x", pos.x);
+    pPrefs->SetInt("window-main-y", pos.y);
+
+    /* save default device selection */
+    int idx = GetSelectedDeviceIndex();
+    if (idx >= 0) {
+        PhoneCollection* pCollection = PhoneCollection::GetInstance();
+        PhoneData* pPhoneData = pCollection->GetPhoneData(idx);
+        pPrefs->SetString("default-device", pPhoneData->GetName());
+    }
+
+    if (mpLogWindow != NULL)
+        mpLogWindow->Close(true);
+    Destroy();
+}
+
+/*
+ * File->Preferences
+ */
+void MainFrame::OnFilePreferences(wxCommandEvent& WXUNUSED(event))
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    PrefsDialog dialog(this);
+    int result;
+
+    result = dialog.ShowModal();
+    if (result == wxID_OK) {
+        /*
+         * The dialog handles writing changes to Preferences, so all we
+         * need to deal with here are changes that have an immediate
+         * impact on us. (which is currently nothing)
+         */
+    }
+}
+
+/*
+ * File->Exit
+ */
+void MainFrame::OnFileExit(wxCommandEvent& WXUNUSED(event))
+{
+    Close(FALSE);       // false means "allow veto"
+}
+
+/*
+ * Decide whether Simulator->Start should be enabled.
+ */
+void MainFrame::OnUpdateSimStart(wxUpdateUIEvent& event)
+{
+    if (IsRuntimeRunning())
+        event.Enable(FALSE);
+    else
+        event.Enable(TRUE);
+}
+
+/*
+ * Simulator->Start
+ */
+void MainFrame::OnSimStart(wxCommandEvent& WXUNUSED(event))
+{
+    // keyboard equivalents can still get here even if menu item disabled
+    if (IsRuntimeRunning())
+        return;
+
+    int id = GetSelectedDeviceIndex();
+    if (id < 0) {
+        fprintf(stderr, "Sim: could not identify currently selected device\n");
+        return;
+    }
+
+#if 0
+    static int foo = 0;
+    foo++;
+    if (foo == 2) {
+        Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+
+        pPrefs->SetBool("debug", true);
+    }
+#endif
+
+    SetupPhoneUI(id, NULL);
+    if (mpPhoneWindow != NULL)
+        mpPhoneWindow->GetDeviceManager()->StartRuntime();
+}
+
+/*
+ * Decide whether Simulator->Stop should be enabled.
+ */
+void MainFrame::OnUpdateSimStop(wxUpdateUIEvent& event)
+{
+    if (IsRuntimeRunning())
+        event.Enable(TRUE);
+    else
+        event.Enable(FALSE);
+}
+
+/*
+ * Simulator->Stop
+ */
+void MainFrame::OnSimStop(wxCommandEvent& WXUNUSED(event))
+{
+    if (!IsRuntimeRunning())
+        return;
+    assert(mpPhoneWindow != NULL);
+    mpPhoneWindow->GetDeviceManager()->StopRuntime();
+}
+
+/*
+ * Decide whether Simulator->Restart should be enabled.
+ */
+void MainFrame::OnUpdateSimRestart(wxUpdateUIEvent& event)
+{
+    if (IsRuntimeRunning())
+        event.Enable(TRUE);
+    else
+        event.Enable(FALSE);
+}
+
+/*
+ * Simulator->Restart - stop then start the device runtime.
+ */
+void MainFrame::OnSimRestart(wxCommandEvent& WXUNUSED(event))
+{
+    if (!IsRuntimeRunning())
+        return;
+
+    printf("Restart requested\n");
+    mpPhoneWindow->GetDeviceManager()->StopRuntime();
+
+    mRestartRequested = true;
+}
+
+/*
+ * Decide whether Simulator->Kill should be enabled.
+ */
+void MainFrame::OnUpdateSimKill(wxUpdateUIEvent& event)
+{
+    if (IsRuntimeKillable())
+        event.Enable(TRUE);
+    else
+        event.Enable(FALSE);
+}
+
+/*
+ * Simulator->Kill
+ */
+void MainFrame::OnSimKill(wxCommandEvent& WXUNUSED(event))
+{
+    if (!IsRuntimeKillable())
+        return;
+    assert(mpPhoneWindow != NULL);
+    mpPhoneWindow->GetDeviceManager()->KillRuntime();
+}
+
+
+/*
+ * Device->[select]
+ */
+void MainFrame::OnDeviceSelected(wxCommandEvent& event)
+{
+    wxBusyCursor busyc;
+    int id = event.GetId() - IDM_DEVICE_SEL0;
+
+    SetupPhoneUI(id, NULL);
+}
+
+/*
+ * Device->Rescan
+ */
+void MainFrame::OnDeviceRescan(wxCommandEvent& event)
+{
+    wxBusyCursor busyc;
+    wxMenuBar* pMenuBar;
+    PhoneCollection* pCollection;
+    wxMenu* pOldMenu;
+    wxMenu* pNewMenu;
+    const char* curDevName = NULL;
+    int idx;
+    
+    /* figure out the current device name */
+    pCollection = PhoneCollection::GetInstance();
+    idx = GetSelectedDeviceIndex();
+    if (idx >= 0) {
+        PhoneData* pPhoneData;
+
+        pPhoneData = pCollection->GetPhoneData(idx);
+        curDevName = pPhoneData->GetName();
+        printf("--- device name is '%s'\n", (const char*) curDevName);
+    }
+
+    /* reconstruct device menu with new data */
+#ifdef BEFORE_ASSET
+    pCollection->ScanForPhones(mSimAssetPath);
+#else
+    pCollection->ScanForPhones(NULL);
+#endif
+
+    pMenuBar = GetMenuBar();
+    idx = pMenuBar->FindMenu(kDeviceMenuString);
+    if (idx == wxNOT_FOUND) {
+        fprintf(stderr, "Sim: couldn't find %s menu\n", (const char*) kDeviceMenuString.ToAscii());
+        return;
+    }
+
+    pNewMenu = CreateDeviceMenu(curDevName);
+
+    pOldMenu = pMenuBar->Replace(idx, pNewMenu, kDeviceMenuString);
+    delete pOldMenu;
+
+    /* tell the PhoneWindow about it; may cause runtime to exit */
+    if (mpPhoneWindow != NULL)
+        mpPhoneWindow->DevicesRescanned();
+}
+
+/*
+ * Set checkbox on menu item.
+ */
+void MainFrame::OnUpdateDebugShowLog(wxUpdateUIEvent& event)
+{
+    if (mpLogWindow == NULL) {
+        event.Enable(false);
+    } else {
+        event.Enable(true);
+        event.Check(mpLogWindow->IsShown());
+    }
+}
+
+/*
+ * Debug->ShowLog toggle.
+ */
+void MainFrame::OnDebugShowLog(wxCommandEvent& WXUNUSED(event))
+{
+    mpLogWindow->Show(!mpLogWindow->IsShown());
+}
+
+/*
+ * Help->Contents
+ */
+void MainFrame::OnHelpContents(wxCommandEvent& WXUNUSED(event))
+{
+    ((MyApp*)wxTheApp)->GetHelpController()->DisplayContents();
+}
+
+/*
+ * Help->About
+ */
+void MainFrame::OnHelpAbout(wxCommandEvent& WXUNUSED(event))
+{
+    wxMessageBox(wxT("Android Simulator v0.1\n"
+                     "Copyright 2006 The Android Open Source Project"),
+        wxT("About..."), wxOK | wxICON_INFORMATION, this);
+}
+
+/*
+ * Sent from phonewindow or when activated
+ */
+void MainFrame::OnActivate(wxActivateEvent& event)
+{
+#if 0
+    if (event.GetActive())
+    {
+        if (mpPhoneWindow != NULL &&
+            mpPhoneWindow->GetDeviceManager()->RefreshRuntime())
+        {
+            wxString msg;
+            int sel;
+
+            msg = wxT("Newer runtime executable found. Would you like to reload the device?");
+
+            sel = wxMessageBox(msg, wxT("Android Safety Patrol"),
+                wxYES | wxNO | wxICON_QUESTION, mpPhoneWindow);
+            //printf("BUTTON was %d (yes=%d)\n", sel, wxYES);
+            if (sel == wxYES)
+            {
+                mpPhoneWindow->GetDeviceManager()->StopRuntime();
+                mpPhoneWindow->Close();
+                mpPhoneWindow = NULL;
+                mRestartRequested = true;
+            }
+            else
+            {
+                mpPhoneWindow->GetDeviceManager()->UserCancelledRefresh();
+            }
+        }
+    }
+#endif
+
+    // let wxWidgets do whatever it needs to do
+    event.Skip();
+}
+            
+
+/*
+ * Device mode selection box.
+ */
+void MainFrame::OnComboBox(wxCommandEvent& event)
+{
+    const char* pref;
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    if (IDC_MODE_SELECT == event.GetId())
+    {
+        int id = GetSelectedDeviceIndex();
+        if (id < 0)
+            return;
+        //printf("--- mode selected: '%s'\n", (const char*) event.GetString().ToAscii());
+
+        /*
+         * Call the phone window's setup function.  Don't call our SetupPhoneUI
+         * function from here -- updating the combo box from a combo box callback
+         * could cause problems.
+         */
+        if (mpPhoneWindow != NULL) {
+            mpPhoneWindow->SetCurrentMode(event.GetString());
+            mpPhoneWindow->Setup(id);
+        }
+    } else if (event.GetId() == IDC_JAVA_VM) {
+        wxComboBox* pBox = (wxComboBox*) FindWindow(IDC_JAVA_VM);
+        pPrefs->SetString("java-vm", pBox->GetValue().ToAscii());
+    }
+}
+
+/*
+ * One of our option checkboxes has been changed.
+ *
+ * We update the prefs database so that the settings are retained when
+ * the simulator is next used.
+ */
+void MainFrame::OnCheckBox(wxCommandEvent& event)
+{
+    const char* pref;
+
+    switch (event.GetId()) {
+    case IDC_USE_GDB:               pref = "debug";                 break;
+    case IDC_USE_VALGRIND:          pref = "valgrind";              break;
+    case IDC_CHECK_JNI:             pref = "check-jni";             break;
+    case IDC_OVERLAY_ONION_SKIN:    pref = "overlay-onion-skin";    break; 
+    default:
+        printf("Sim: unrecognized checkbox %d in OnCheckBox\n", event.GetId());
+        return;
+    }
+
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    pPrefs->SetBool(pref, (bool) event.GetInt());
+    //printf("--- set pref '%s' to %d\n", pref, (bool) event.GetInt());
+    if (event.GetId() == IDC_OVERLAY_ONION_SKIN) {
+        BroadcastOnionSkinUpdate();
+    }
+    if (event.GetId() == IDC_CHECK_JNI) {
+        const char* val = "0";
+        if ((bool) event.GetInt())
+            val = "1";
+        mPropertyServerThread->SetProperty(PropertyServer::kPropCheckJni, val);
+
+    }
+}
+
+void MainFrame::BroadcastOnionSkinUpdate() {
+    if (mpPhoneWindow != NULL) {
+        // broadcast a user event indicating an onion skin update
+        UserEvent uev(0, (void*) -1);
+        mpPhoneWindow->GetDeviceManager()->BroadcastEvent(uev);
+    }
+}
+
+/*
+ * A text control on the main page is being updated.
+ *
+ * The current implementation updates the preferences database on every
+ * change, which is a bit silly but is easy to do.
+ */
+void MainFrame::OnText(wxCommandEvent& event)
+{
+    const char* pref;
+
+    switch (event.GetId()) {
+    case IDC_JAVA_APP_NAME:     pref = "java-app-name"; break;
+    default:
+        printf("Sim: unrecognized textctrl %d in OnText\n", event.GetId());
+        return;
+    }
+
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    // event.GetString() does not work on Mac -- always blank
+    //pPrefs->SetString(pref, event.GetString());
+    assert(event.GetId() == IDC_JAVA_APP_NAME); // fix if we add more
+    wxComboBox* pBox;
+    pBox = (wxComboBox*) FindWindow(IDC_JAVA_APP_NAME);
+    pPrefs->SetString(pref, pBox->GetValue().ToAscii());
+    //printf("--- set pref '%s' to '%s'\n", pref,(const char*)pBox->GetValue());
+}
+
+/*
+ * A user pressed enter in a text control on the main page.
+ *
+ * The current implementation updates the preferences database on every
+ * change, which is a bit silly but is easy to do.
+ */
+void MainFrame::OnTextEnter(wxCommandEvent& event)
+{
+    const char* pref;
+
+    switch (event.GetId()) {
+    case IDC_ONION_SKIN_FILE_NAME:
+        pref = "onion-skin-file-name";
+        break;
+    default:
+        printf("Sim: unrecognized textctrl %d in OnTextEnter\n", event.GetId());
+        return;
+    }
+
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    assert(event.GetId() == IDC_ONION_SKIN_FILE_NAME); // fix if we add more
+    wxTextCtrl* pTextCtrl;
+    pTextCtrl = (wxTextCtrl*) FindWindow(IDC_ONION_SKIN_FILE_NAME);
+    wxString onionSkinFileNameWxString = pTextCtrl->GetValue();
+    char* onionSkinFileName = "";
+    if (onionSkinFileNameWxString.Len() > 0) {
+        onionSkinFileName = android::strdupNew(onionSkinFileNameWxString.ToAscii());
+    }
+    pPrefs->SetString(pref, onionSkinFileName);
+    BroadcastOnionSkinUpdate();
+}
+
+/*
+ * A user pressed a button on the main page
+ * 
+ */
+ void MainFrame::OnButton(wxCommandEvent& event)
+ {
+    wxWindow* base;
+    wxFileDialog* pOnionSkinFileChooser;
+    int retVal;
+    switch (event.GetId()) {
+    case IDC_ONION_SKIN_BUTTON:
+        base = FindWindow(IDC_ONION_SKIN_BUTTON)->GetParent();
+        pOnionSkinFileChooser = new wxFileDialog(base, 
+            wxT("Choose the onion skin image file."), 
+            wxT(""), wxT(""), wxT("*.*"),
+            wxOPEN | wxFILE_MUST_EXIST);
+        retVal = pOnionSkinFileChooser->ShowModal();
+        if (retVal == pOnionSkinFileChooser->GetAffirmativeId()) {
+            Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+            assert(pPrefs != NULL);
+            wxString fileNameWxString = pOnionSkinFileChooser->GetPath();
+            const char* fileName = android::strdupNew(fileNameWxString.ToAscii());
+            wxTextCtrl* fileTextCtrl = (wxTextCtrl*) FindWindow(IDC_ONION_SKIN_FILE_NAME);
+            fileTextCtrl->SetValue(fileNameWxString);
+            pPrefs->SetString("onion-skin-file-name", fileName);
+            BroadcastOnionSkinUpdate();
+        }
+        break;
+    default:
+        printf("Sim: unrecognized button %d in OnButton\n", event.GetId());
+        return;
+    }     
+ }
+ 
+ /*
+  * The user moved a slider on the main page
+  */
+ void MainFrame::OnSliderChange(wxScrollEvent& event)
+ {
+    wxSlider* pSlider;
+    Preferences* pPrefs;
+    switch (event.GetId()) {
+    case IDC_ONION_SKIN_ALPHA_VAL:
+        pSlider = (wxSlider*) FindWindow(IDC_ONION_SKIN_ALPHA_VAL);
+        pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+        assert(pPrefs != NULL);
+        pPrefs->SetInt("onion-skin-alpha-value", pSlider->GetValue());
+        BroadcastOnionSkinUpdate();
+        break;
+    default:
+        printf("Sim: unrecognized scroller or slider %d in OnSliderChange\n", event.GetId());
+        return;
+    }     
+ }
+
+#if 0
+/*
+ * Idle processing.  Under wxWidgets this only called once after UI
+ * activity unless you call event.RequestMore().
+ */
+void MainFrame::OnIdle(wxIdleEvent& event)
+{
+    event.Skip();       // let base class handler do stuff
+}
+#endif
+
+/*
+ * Handle the timer.
+ *
+ * This is being called in the main thread, so multithreading with the
+ * rest of MainFrame isn't a concern here.
+ */
+void MainFrame::OnTimer(wxTimerEvent& event)
+{
+    bool status;
+
+    /*
+     * Check to see if the runtime died without telling us.  This can only
+     * happen if we forcibly kill our thread.  We shouldn't really be
+     * doing that anymore, but keep this in for now just in case.
+     */
+    status = IsRuntimeRunning();
+
+    if (mSimRunning != status) {
+        if (!status) {
+            printf("Sim: fixed mSimRunning=%d actual=%d\n",
+                mSimRunning, status);
+            mSimRunning = status;
+
+            if (!status)
+                HandleRuntimeStop();
+        } else {
+            /*
+             * This was happening when we were shutting down but the
+             * device management thread hadn't completely gone away.  The
+             * simple IsRunning test passes, so we get a false positive.
+             * Ignore it.
+             */
+        }
+    }
+
+    if (gWantToKill) {
+        if (IsRuntimeRunning()) {
+            printf("Sim: handling kill request\n");
+            mpPhoneWindow->GetDeviceManager()->KillRuntime();
+        }
+        gWantToKill = false;
+
+        /* see if Ctrl-C should kill us too */
+        Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+        bool die = false;
+
+        pPrefs->GetBool("trap-sigint-suicide", &die);
+        if (die) {
+            printf("Sim: goodbye cruel world!\n");
+            exit(0);
+        }
+    }
+}
+
+/*
+ * Determine whether or not the simulator is running.
+ */
+bool MainFrame::IsRuntimeRunning(void)
+{
+    bool result;
+
+    if (mpPhoneWindow == NULL)
+        result = false;
+    else if (!mpPhoneWindow->IsReady())
+        result = false;
+    else
+        result = mpPhoneWindow->GetDeviceManager()->IsRunning();
+
+    return result;
+}
+
+/*
+ * Determine whether or not the runtime can be killed.
+ */
+bool MainFrame::IsRuntimeKillable(void)
+{
+    bool result;
+
+    result = IsRuntimeRunning();
+    if (result)
+        result = mpPhoneWindow->GetDeviceManager()->IsKillable();
+
+    return result;
+}
+
+/*
+ * Determine whether two devices are sufficiently compatible.
+ */
+bool MainFrame::CompatibleDevices(PhoneData* pData1, PhoneData* pData2)
+{
+    int displayCount;
+
+    displayCount = pData1->GetNumDisplays();
+    if (pData2->GetNumDisplays() != displayCount)
+        return false;
+
+    for (int i = 0; i < displayCount; i++) {
+        PhoneDisplay* pDisplay1 = pData1->GetPhoneDisplay(i);
+        PhoneDisplay* pDisplay2 = pData2->GetPhoneDisplay(i);
+
+        if (!PhoneDisplay::IsCompatible(pDisplay1, pDisplay2))
+            return false;
+    }
+
+    return true;
+}
+
+/*
+ * (Re-)arrange the UI for the currently selected phone model.
+ *
+ * If the simulator is running, and the set of displays for the current
+ * device are incompatible with the new device, we need to restart the
+ * runtime.  We need to ask for permission first though.
+ */
+void MainFrame::SetupPhoneUI(int idx, const char* defaultMode)
+{
+    PhoneCollection* pCollection;
+    PhoneData* pPhoneData;
+    wxString* choices = NULL;
+    int numChoices = 0;
+    int numKeyboards = 0;
+    bool haveDefaultMode = false;
+    wxCharBuffer currentMode;
+    int i;
+
+    pCollection = PhoneCollection::GetInstance();
+    pPhoneData = pCollection->GetPhoneData(idx);
+    if (pPhoneData == NULL) {
+        fprintf(stderr, "ERROR: device index %d not valid\n", idx);
+        goto bail;
+    }
+
+    /*
+     * We have a window up.  If the displays aren't compatible, we'll
+     * need to recreate it.
+     */
+    if (mpPhoneWindow != NULL) {
+        PhoneData* pCurData = mpPhoneWindow->GetPhoneData();
+
+        if (!CompatibleDevices(pCurData, pPhoneData)) {
+            /*
+             * We need to trash the window.  This will also kill the
+             * runtime.  If it's running, ask permission.
+             */
+            if (IsRuntimeRunning()) {
+                wxString msg;
+                int sel;
+
+                msg =  wxT("Switching to the new device requires restarting the");
+                msg += wxT(" runtime.  Continue?");
+
+                sel = wxMessageBox(msg, wxT("Android Safety Patrol"),
+                    wxOK | wxCANCEL | wxICON_QUESTION, this);
+                printf("BUTTON was %d (ok=%d)\n", sel, wxOK);
+                if (sel == wxCANCEL)
+                    goto bail;
+
+                /* shut it down (politely), ask for an eventual restart */
+                mpPhoneWindow->GetDeviceManager()->StopRuntime();
+                mpPhoneWindow->Close();
+                mpPhoneWindow = NULL;
+                mRestartRequested = true;
+                goto bail;
+            } else {
+                /* not running, just trash the window and continue */
+                mpPhoneWindow->Close();
+                mpPhoneWindow = NULL;
+            }
+        }
+    }
+
+    /*
+     * Figure out the set of available modes.
+     */
+
+    numChoices = pPhoneData->GetNumModes();
+    if (numChoices > 0) {
+        choices = new wxString[numChoices];
+        for (i = 0; i < numChoices; i++) {
+            PhoneMode* pPhoneMode;
+            pPhoneMode = pPhoneData->GetPhoneMode(i);
+            choices[i] = wxString::FromAscii(pPhoneMode->GetName());
+            if (defaultMode != NULL &&
+                strcmp(defaultMode, pPhoneMode->GetName()) == 0)
+            {
+                haveDefaultMode = true;
+            }
+        }
+    }
+
+    if (choices == NULL) {
+        /* had a failure earlier; configure UI with default stuff */
+        choices = new wxString[1];
+        choices[0] = wxT("(none)");
+    }
+
+    if (!haveDefaultMode) {
+        /*
+         * Default mode wasn't found.  If we specify it as the default
+         * in the wxComboBox create call it shows up in the combo box
+         * under Linux, even if it doesn't exist in the list.  So, we
+         * make sure that it doesn't get used if we can't find it.
+         */
+        if (defaultMode != NULL) {
+            printf("Sim: HEY: default mode '%s' not found in list\n",
+                defaultMode);
+        }
+        currentMode = choices[0].ToAscii();
+    } else {
+        currentMode = defaultMode;
+    }
+
+
+    /*
+     * Create the window if necessary.
+     */
+    if (mpPhoneWindow == NULL) {
+        // create, setup, and then show window
+        mpPhoneWindow = new PhoneWindow(this, mPhoneWindowPosn);
+        mpPhoneWindow->SetCurrentMode((const char*)currentMode);
+        if (!mpPhoneWindow->Setup(idx)) {
+            delete mpPhoneWindow;
+            mpPhoneWindow = NULL;
+        }
+        if (mpPhoneWindow != NULL) {
+            mpPhoneWindow->Show();
+            //mpPhoneWindow->CheckPlacement();
+        }
+    } else {
+        // just set up for new device
+        mpPhoneWindow->SetCurrentMode((const char*)currentMode);
+        if (!mpPhoneWindow->Setup(idx)) {
+            // it's in an uncertain state, blow it away
+            delete mpPhoneWindow;
+            mpPhoneWindow = NULL;
+        }
+    }
+
+    /*
+     * Reconfigure mode selection box.
+     */
+    wxComboBox* pModeSelection;
+    pModeSelection = (wxComboBox*)FindWindow(IDC_MODE_SELECT);
+    pModeSelection->Clear();
+    for (i = 0; i < numChoices; i++)
+        pModeSelection->Append(choices[i]);
+    pModeSelection->SetSelection(0);
+    pModeSelection->Enable(numChoices > 1);
+    
+    /*
+     * configure qwerty keyboard attribute
+     */
+    numKeyboards = pPhoneData->GetNumKeyboards();
+    if (numKeyboards > 0) {
+        // only use the first keyboard for now
+        PhoneKeyboard* pPhoneKeyboard;
+        pPhoneKeyboard = pPhoneData->GetPhoneKeyboard(0);
+        if (pPhoneKeyboard->getQwerty()) {
+            printf("Sim: set 'qwerty' env\n");
+            setenv("qwerty", "true", true);
+        }
+    }
+    
+bail:
+    delete[] choices;
+}
+
+/*
+ * Figure out which device is currently selected.
+ *
+ * The easiest way to do this is just run down the list of possible IDs
+ * and stop when something claims to be checked.
+ *
+ * Returns -1 if it can't find a checked item (which can happen if no
+ * device layouts were found).
+ */
+int MainFrame::GetSelectedDeviceIndex(void)
+{
+    wxMenuBar* pMenuBar;
+    wxMenu* pMenu;
+    int idx;
+    
+    pMenuBar = GetMenuBar();
+    idx = pMenuBar->FindMenu(kDeviceMenuString);
+    if (idx == wxNOT_FOUND) {
+        fprintf(stderr, "Sim: couldn't find %s menu\n", (const char*) kDeviceMenuString.ToAscii());
+        return -1;
+    }
+
+    pMenu = pMenuBar->GetMenu(idx);
+
+    //printf("Menu.MenuItemCount = %d\n", pMenu->GetMenuItemCount());
+    for (int j = pMenu->GetMenuItemCount() -1; j >= 0; j--) {
+        wxMenuItem* pItem;
+
+        pItem = pMenu->FindItemByPosition(j);
+        //printf("ITEM %d: %s\n", j, (const char*) pItem->GetLabel());
+        if (pItem->IsChecked()) {
+            printf("Sim: selected device is '%s'\n",
+                (const char*) pItem->GetLabel().ToAscii());
+            return j;
+        }
+    }
+
+    return -1;
+}
+
+/*
+ * Receive a status message from the runtime thread.
+ */
+void MainFrame::OnUserEvent(UserEvent& event)
+{
+    UserEventMessage* pUem;
+
+    pUem = (UserEventMessage*) event.GetData();
+    assert(pUem != NULL);
+
+    switch (pUem->GetType()) {
+    case UserEventMessage::kRuntimeStarted:
+        printf("Sim: runtime thread started!\n");
+        HandleRuntimeStart();
+        break;
+    case UserEventMessage::kRuntimeStopped:
+        printf("Sim: runtime thread stopped!\n");
+        HandleRuntimeStop();
+        break;
+    case UserEventMessage::kErrorMessage:
+        {
+            wxString msg = pUem->GetString();
+            wxMessageBox(msg, wxT("Android Runtime Error"),
+                wxOK | wxICON_WARNING, this);
+        }
+        break;
+    case UserEventMessage::kLogMessage:
+        mpLogWindow->AddLogMessage(pUem->GetLogMessage());
+        break;
+    case UserEventMessage::kExternalRuntime:
+        HandleExternalRuntime(pUem->GetReader(), pUem->GetWriter());
+        break;
+    default:
+        printf("Sim: MESSAGE: unknown UserEventMessage rcvd (type=%d)\n",
+            pUem->GetType());
+        break;
+    }
+
+    delete pUem;
+}
+
+/*
+ * The device management thread is up, so the runtime should be fully
+ * running shortly.
+ */
+void MainFrame::HandleRuntimeStart(void)
+{
+    mSimRunning = true;
+
+    SetStatusText(kStatusRunning, 1);
+}
+
+/*
+ * The device management thread is exiting, so the runtime must be dead.
+ */
+void MainFrame::HandleRuntimeStop(void)
+{
+    mSimRunning = false;
+
+    SetStatusText(kStatusNotRunning, 1);
+
+    if (mRestartRequested) {
+        printf("Sim: restarting runtime\n");
+        mRestartRequested = false;
+        SetupPhoneUI(GetSelectedDeviceIndex(), NULL);
+        if (mpPhoneWindow != NULL)
+            mpPhoneWindow->GetDeviceManager()->StartRuntime();
+    }
+}
+
+/*
+ * Handle a connection from an external runtime.
+ */
+void MainFrame::HandleExternalRuntime(android::Pipe* reader,
+    android::Pipe* writer)
+{
+    android::MessageStream msgStream;
+    android::Message msg;
+
+    if (IsRuntimeRunning()) {
+        /*
+         * Tell the new guy to go away.
+         */
+        if (!msgStream.init(reader, writer, true)) {
+            fprintf(stderr, "Sim: WARNING: unable to talk to remote runtime\n");
+            goto bail;
+        }
+
+        printf("Sim: telling external runtime to go away\n");
+        msg.setCommand(android::Simulator::kCommandGoAway, 0);
+        msgStream.send(&msg);
+    } else {
+        printf("Sim: new external runtime wants to talk to us\n");
+
+        /*
+         * Launch the pieces necessary to talk to this guy.
+         */
+        int id = GetSelectedDeviceIndex();
+        if (id < 0) {
+            fprintf(stderr,
+                "Sim: could not identify currently selected device\n");
+            goto bail;
+        }
+
+        /* kill existing window, so it pops up and reclaims focus */
+        if (mpPhoneWindow != NULL) {
+            Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+            bool okay;
+
+            if (pPrefs->GetBool("refocus-on-restart", &okay) && okay) {
+                printf("Sim: inducing phone window refocus\n");
+                mpPhoneWindow->Close(TRUE);     // no veto
+                mpPhoneWindow = NULL;
+            }
+        }
+
+        SetupPhoneUI(id, NULL);
+        if (mpPhoneWindow != NULL) {
+            mpPhoneWindow->GetDeviceManager()->StartRuntime(reader, writer);
+        } else {
+            fprintf(stderr, "Sim: ERROR: unable to get runtime going\n");
+            goto bail;
+        }
+
+        // we don't own these anymore
+        reader = writer = NULL;
+    }
+
+bail:
+    delete reader;
+    delete writer;
+}
+
+/*
+ * The phone window is about to destroy itself.  Get rid of our pointer
+ * to it, and record its last position so we can create the new one in
+ * the same place.
+ */
+void MainFrame::PhoneWindowClosing(int x, int y)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+
+    mpPhoneWindow = NULL;
+
+    mPhoneWindowPosn.x = x;
+    mPhoneWindowPosn.y = y;
+
+    pPrefs->SetInt("window-device-x", x);
+    pPrefs->SetInt("window-device-y", y);
+}
+
diff --git a/simulator/app/MainFrame.h b/simulator/app/MainFrame.h
new file mode 100644
index 0000000..817a182
--- /dev/null
+++ b/simulator/app/MainFrame.h
@@ -0,0 +1,115 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Main window declaration.
+//
+#ifndef _SIM_MAINFRAME_H
+#define _SIM_MAINFRAME_H
+
+#include "PhoneWindow.h"
+#include "DeviceWindow.h"
+#include "LogWindow.h"
+#include "ExternalRuntime.h"
+#include "PropertyServer.h"
+
+/*
+ * Main window.
+ */
+class MainFrame : public wxFrame {
+public:
+    /* define a constructor so we can set up menus */
+    MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size,
+        long style);
+    virtual ~MainFrame(void);
+
+    /* called by modeless phone window dialog when it closes */
+    void PhoneWindowClosing(int x, int y);
+
+    void Vibrate(int vibrateOn) { mpPhoneWindow->Vibrate(vibrateOn); }
+
+    PropertyServer* GetPropertyServer(void) { return mPropertyServerThread; }
+
+private:
+    void ConstructMenu(void);
+    void ConstructControls(void);
+
+    void OnClose(wxCloseEvent& event);
+    void OnTimer(wxTimerEvent& event);
+    //void OnIdle(wxIdleEvent& event);
+    void OnActivate(wxActivateEvent& event);
+    void OnButton(wxCommandEvent& event);
+    void OnComboBox(wxCommandEvent& event);
+    void OnCheckBox(wxCommandEvent& event);
+    void OnText(wxCommandEvent& event);
+    void OnTextEnter(wxCommandEvent& event);
+    void OnUserEvent(UserEvent& event);
+    void OnSliderChange(wxScrollEvent& event);
+
+    void OnFilePreferences(wxCommandEvent& event);
+    void OnFileExit(wxCommandEvent& event);
+    void OnUpdateSimStart(wxUpdateUIEvent& event);
+    void OnSimStart(wxCommandEvent& event);
+    void OnUpdateSimStop(wxUpdateUIEvent& event);
+    void OnSimStop(wxCommandEvent& event);
+    void OnUpdateSimReload(wxUpdateUIEvent& event);
+    void OnSimReload(wxCommandEvent& event);
+    void OnUpdateSimRestart(wxUpdateUIEvent& event);
+    void OnSimRestart(wxCommandEvent& event);
+    void OnUpdateSimKill(wxUpdateUIEvent& event);
+    void OnSimKill(wxCommandEvent& event);
+    void OnDeviceSelected(wxCommandEvent& event);
+    void OnDeviceRescan(wxCommandEvent& event);
+    void OnUpdateDebugShowLog(wxUpdateUIEvent& event);
+    void OnDebugShowLog(wxCommandEvent& event);
+    void OnHelpContents(wxCommandEvent& event);
+    void OnHelpAbout(wxCommandEvent& event);
+
+    wxMenu* CreateDeviceMenu(const char* defaultItemName);
+    void SetCheckFromPref(wxCheckBox* pControl, const char* prefStr,
+        bool defaultVal);
+
+    void UpdateRuntimeExeStr(void);
+
+    /* prep the phone UI; "defaultMode" may be NULL */
+    void SetupPhoneUI(int idx, const char* defaultMode);
+
+    bool CompatibleDevices(PhoneData* pData1, PhoneData* pData2);
+
+    void HandleRuntimeStart(void);
+    void HandleRuntimeStop(void);
+    void HandleExternalRuntime(android::Pipe* reader, android::Pipe* writer);
+
+    int GetSelectedDeviceIndex(void);
+    bool IsRuntimeRunning(void);
+    bool IsRuntimeKillable(void);
+
+    void BroadcastOnionSkinUpdate(void);
+    
+    bool    mSimRunning;
+    bool    mRestartRequested;
+
+    enum { kHalfSecondTimerId = 1000 };
+
+    wxString        mSimAssetPath;
+
+    /* if we have a phone running, this points to its state */
+    PhoneWindow*    mpPhoneWindow;
+
+    /* phone window position */
+    wxPoint         mPhoneWindowPosn;
+
+    /* window that captures log output */
+    LogWindow*      mpLogWindow;
+
+    wxTimer         mTimer;
+
+    /* watches for connection from runtime */
+    ExternalRuntime* mExternalRuntimeThread;
+
+    /* serve up system properties */
+    PropertyServer*  mPropertyServerThread;
+
+    DECLARE_EVENT_TABLE()
+};
+
+#endif // _SIM_MAINFRAME_H
diff --git a/simulator/app/MessageStream.cpp b/simulator/app/MessageStream.cpp
new file mode 100644
index 0000000..2397c63
--- /dev/null
+++ b/simulator/app/MessageStream.cpp
@@ -0,0 +1,402 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Message stream abstraction.
+//
+#include "MessageStream.h"
+#include "LogBundle.h"
+
+#include "utils/Log.h"
+
+#include <string.h>
+#include <assert.h>
+
+using namespace android;
+
+/*
+ * ===========================================================================
+ *      Message
+ * ===========================================================================
+ */
+
+/*
+ * Send a blob of raw data.
+ */
+void Message::setRaw(const unsigned char* data, int len, Cleanup cleanup)
+{
+    reset();
+
+    mData = const_cast<unsigned char*>(data);
+    mLength = len;
+    mCleanup = cleanup;
+    mType = kTypeRaw;
+}
+
+/*
+ * Send a "name=value" config pair.
+ */
+void Message::setConfig(const char* name, const char* value)
+{
+    reset();
+
+    assert(name != NULL && value != NULL);
+
+    int nlen = strlen(name) +1;
+    int vlen = strlen(value) +1;
+    mData = new unsigned char[nlen+vlen];
+    mCleanup = kCleanupDelete;
+    mLength = nlen + vlen;
+    mType = kTypeConfig;
+
+    memcpy(mData, name, nlen);
+    memcpy(mData + nlen, value, vlen);
+}
+
+/*
+ * Try to return the contents of the message as if it were a name/value pair.
+ */
+bool Message::getConfig(const char** pName, const char** pValue)
+{
+    if (mLength < 2)
+        return false;
+    assert(mData != NULL);
+
+    *pName = (const char*) mData;
+    *pValue = (const char*) (mData + strlen((char*)mData) +1);
+    return true;
+}
+
+/*
+ * Send a command/arg pair.
+ */
+void Message::setCommand(int cmd, int arg)
+{
+    reset();
+
+    mData = new unsigned char[sizeof(int) * 2];
+    mCleanup = kCleanupDelete;
+    mLength = sizeof(int) * 2;
+    mType = kTypeCommand;
+
+    int* pInt = (int*) mData;
+    pInt[0] = cmd;
+    pInt[1] = arg;
+}
+
+/*
+ * Send a command with 3 args instead of just one.
+ */
+void Message::setCommandExt(int cmd, int arg0, int arg1, int arg2)
+{
+    reset();
+
+    mData = new unsigned char[sizeof(int) * 4];
+    mCleanup = kCleanupDelete;
+    mLength = sizeof(int) * 4;
+    mType = kTypeCommandExt;
+
+    int* pInt = (int*) mData;
+    pInt[0] = cmd;
+    pInt[1] = arg0;
+    pInt[2] = arg1;
+    pInt[3] = arg2;
+}
+
+/*
+ * Try to return the contents of the message as if it were a "command".
+ */
+bool Message::getCommand(int* pCmd, int* pArg)
+{
+    if (mLength != sizeof(int) * 2) {
+        LOG(LOG_WARN, "", "type is %d, len is %d\n", mType, mLength);
+        return false;
+    }
+    assert(mData != NULL);
+
+    const int* pInt = (const int*) mData;
+    *pCmd = pInt[0];
+    *pArg = pInt[1];
+
+    return true;
+}
+
+/*
+ * Serialize a log message.
+ *
+ * DO NOT call LOG() from here.
+ */
+void Message::setLogBundle(const android_LogBundle* pBundle)
+{
+    reset();
+
+    /* get string lengths; we add one here to include the '\0' */
+    int tagLen, msgLen;
+    tagLen = strlen(pBundle->tag) + 1;
+    size_t i;
+    msgLen = 0;
+    for (i=0; i<pBundle->msgCount; i++) msgLen += pBundle->msgVec[i].iov_len;
+    msgLen += 1;
+
+    /* set up the structure */
+    mCleanup = kCleanupDelete;
+    mLength =   sizeof(pBundle->when) +
+                sizeof(pBundle->priority) +
+                sizeof(pBundle->pid) +
+                tagLen +
+                msgLen;
+    mData = new unsigned char[mLength];
+    mType = kTypeLogBundle;
+
+    unsigned char* pCur = mData;
+
+    /* copy the stuff over */
+    *((time_t*)pCur) = pBundle->when;
+    pCur += sizeof(pBundle->when);
+    *((android_LogPriority*)pCur) = pBundle->priority;
+    pCur += sizeof(pBundle->priority);
+    *((pid_t*)pCur) = pBundle->pid;
+    pCur += sizeof(pBundle->pid);
+    memcpy(pCur, pBundle->tag, tagLen);
+    pCur += tagLen;
+    for (i=0; i<pBundle->msgCount; i++) {
+        memcpy(pCur, pBundle->msgVec[i].iov_base, pBundle->msgVec[i].iov_len);
+        pCur += pBundle->msgVec[i].iov_len;
+    }
+    *pCur++ = 0;
+
+    assert(pCur - mData == mLength);
+}
+
+/*
+ * Extract the components of a log bundle.
+ *
+ * We're just returning points inside the message buffer, so the caller
+ * will need to copy them out before the next reset().
+ */
+bool Message::getLogBundle(android_LogBundle* pBundle)
+{
+    if (mLength < (int)(sizeof(time_t) + sizeof(int)*2 + 4)) {
+        LOG(LOG_WARN, "", "type is %d, len is %d, too small\n",
+            mType, mLength);
+        return false;
+    }
+    assert(mData != NULL);
+
+    unsigned char* pCur = mData;
+
+    pBundle->when = *((time_t*) pCur);
+    pCur += sizeof(pBundle->when);
+    pBundle->priority = *((android_LogPriority*) pCur);
+    pCur += sizeof(pBundle->priority);
+    pBundle->pid = *((pid_t*) pCur);
+    pCur += sizeof(pBundle->pid);
+    pBundle->tag = (const char*) pCur;
+    pCur += strlen((const char*) pCur) +1;
+    mVec.iov_base = (char*) pCur;
+    mVec.iov_len = strlen((const char*) pCur);
+    pBundle->msgVec = &mVec;
+    pBundle->msgCount = 1;
+    pCur += mVec.iov_len +1;
+
+    if (pCur - mData != mLength) {
+        LOG(LOG_WARN, "", "log bundle rcvd %d, used %d\n", mLength,
+            (int) (pCur - mData));
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Read the next event from the pipe.
+ *
+ * This is not expected to work well when multiple threads are reading.
+ */
+bool Message::read(Pipe* pPipe, bool wait)
+{
+    if (pPipe == NULL)
+        return false;
+    assert(pPipe->isCreated());
+
+    if (!wait) {
+        if (!pPipe->readReady())
+            return false;
+    }
+
+    reset();
+
+    unsigned char header[4];
+    if (pPipe->read(header, 4) != 4)
+        return false;
+
+    mType = (MessageType) header[2];
+    mLength = header[0] | header[1] << 8;
+    mLength -= 2;   // we already read two of them in the header
+
+    if (mLength > 0) {
+        int actual;
+
+        mData = new unsigned char[mLength];
+        if (mData == NULL) {
+            LOG(LOG_ERROR, "", "alloc failed\n");
+            return false;
+        }
+        mCleanup = kCleanupDelete;
+
+        actual = pPipe->read(mData, mLength);
+        if (actual != mLength) {
+            LOG(LOG_WARN, "", "failed reading message body (%d of %d bytes)\n",
+                actual, mLength);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Write this event to a pipe.
+ *
+ * It would be easiest to write the header and message body with two
+ * separate calls, but that will occasionally fail on multithreaded
+ * systems when the writes are interleaved.  We have to allocate a
+ * temporary buffer, copy the data, and write it all at once.  This
+ * would be easier with writev(), but we can't rely on having that.
+ *
+ * DO NOT call LOG() from here, as we could be in the process of sending
+ * a log message.
+ */
+bool Message::write(Pipe* pPipe) const
+{
+    char tmpBuf[128];
+    char* writeBuf = tmpBuf;
+    bool result = false;
+    int kHeaderLen = 4;
+
+    if (pPipe == NULL)
+        return false;
+    assert(pPipe->isCreated());
+
+    if (mData == NULL || mLength < 0)
+        return false;
+
+    /* if it doesn't fit in stack buffer, allocate space */
+    if (mLength + kHeaderLen > (int) sizeof(tmpBuf)) {
+        writeBuf = new char[mLength + kHeaderLen];
+        if (writeBuf == NULL)
+            goto bail;
+    }
+
+    /*
+     * The current value of "mLength" does not include the 4-byte header.
+     * Two of the 4 header bytes are included in the length we output
+     * (the type byte and the pad byte), so we adjust mLength.
+     */
+    writeBuf[0] = (unsigned char) (mLength + kHeaderLen -2);
+    writeBuf[1] = (unsigned char) ((mLength + kHeaderLen -2) >> 8);
+    writeBuf[2] = (unsigned char) mType;
+    writeBuf[3] = 0;
+    if (mLength > 0)
+        memcpy(writeBuf + kHeaderLen, mData, mLength);
+
+    int actual;
+
+    actual = pPipe->write(writeBuf, mLength + kHeaderLen);
+    if (actual != mLength + kHeaderLen) {
+        fprintf(stderr,
+            "Message::write failed writing message body (%d of %d bytes)\n",
+            actual, mLength + kHeaderLen);
+        goto bail;
+    }
+
+    result = true;
+
+bail:
+    if (writeBuf != tmpBuf)
+        delete[] writeBuf;
+    return result;
+}
+
+
+/*
+ * ===========================================================================
+ *      MessageStream
+ * ===========================================================================
+ */
+
+/*
+ * Get ready to go.
+ */
+bool MessageStream::init(Pipe* readPipe, Pipe* writePipe, bool initiateHello)
+{
+    assert(mReadPipe == NULL && mWritePipe == NULL);    // only once
+
+    /*
+     * Swap "hello" messages.
+     *
+     * In a more robust implementation, this would include version numbers
+     * and capability flags.
+     */
+    if (initiateHello) {
+        long data = kHelloMsg;
+        Message msg;
+
+        /* send hello */
+        msg.setRaw((unsigned char*) &data, sizeof(data),
+            Message::kCleanupNoDelete);
+        if (!msg.write(writePipe)) {
+            LOG(LOG_WARN, "", "hello write failed in stream init\n");
+            return false;
+        }
+
+        LOG(LOG_DEBUG, "", "waiting for peer to ack my hello\n");
+
+        /* wait for the ack */
+        if (!msg.read(readPipe, true)) {
+            LOG(LOG_WARN, "", "hello ack read failed in stream init\n");
+            return false;
+        }
+
+        const long* pAck;
+        pAck = (const long*) msg.getData();
+        if (pAck == NULL || *pAck != kHelloAckMsg) {
+            LOG(LOG_WARN, "", "hello ack was bad\n");
+            return false;
+        }
+    } else {
+        long data = kHelloAckMsg;
+        Message msg;
+
+        LOG(LOG_DEBUG, "", "waiting for hello from peer\n");
+
+        /* wait for the hello */
+        if (!msg.read(readPipe, true)) {
+            LOG(LOG_WARN, "", "hello read failed in stream init\n");
+            return false;
+        }
+
+        const long* pAck;
+        pAck = (const long*) msg.getData();
+        if (pAck == NULL || *pAck != kHelloMsg) {
+            LOG(LOG_WARN, "", "hello was bad\n");
+            return false;
+        }
+
+        /* send hello ack */
+        msg.setRaw((unsigned char*) &data, sizeof(data),
+            Message::kCleanupNoDelete);
+        if (!msg.write(writePipe)) {
+            LOG(LOG_WARN, "", "hello ack write failed in stream init\n");
+            return false;
+        }
+    }
+
+    /* success, set up our local stuff */
+    mReadPipe = readPipe;
+    mWritePipe = writePipe;
+
+    //LOG(LOG_DEBUG, "", "init success\n");
+
+    return true;
+}
+
diff --git a/simulator/app/MessageStream.h b/simulator/app/MessageStream.h
new file mode 100644
index 0000000..de9c398
--- /dev/null
+++ b/simulator/app/MessageStream.h
@@ -0,0 +1,190 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// High-level message stream that sits on top of a pair of Pipes.  Useful
+// for inter-process communication, e.g. between "simulator" and "runtime".
+//
+// All messages are sent in packets:
+//  +00 16-bit length (of everything that follows), little-endian
+//  +02 8-bit message type
+//  +03 (reserved, must be zero)
+//  +04 message body
+//
+#ifndef _LIBS_UTILS_MESSAGE_STREAM_H
+#define _LIBS_UTILS_MESSAGE_STREAM_H
+
+#ifdef HAVE_ANDROID_OS
+#error DO NOT USE THIS FILE IN THE DEVICE BUILD
+#endif
+
+#include <utils/Pipe.h>
+#include <stdlib.h>
+#include <cutils/uio.h>
+
+// Defined in LogBundle.h.
+struct android_LogBundle;
+
+namespace android {
+
+/*
+ * A single message, which can be filled out and sent, or filled with
+ * received data.
+ *
+ * Message objects are reusable.
+ */
+class Message {
+public:
+    Message(void)
+        : mCleanup(kCleanupUnknown)
+        { reset(); }
+    ~Message(void) { reset(); }
+
+    /* values for message type byte */
+    typedef enum MessageType {
+        kTypeUnknown = 0,
+        kTypeRaw,           // chunk of raw data
+        kTypeConfig,        // send a name=value pair to peer
+        kTypeCommand,       // simple command w/arg
+        kTypeCommandExt,    // slightly more complicated command
+        kTypeLogBundle,     // multi-part log message
+    } MessageType;
+
+    /* what to do with data when we're done */
+    typedef enum Cleanup {
+        kCleanupUnknown = 0,
+        kCleanupNoDelete,   // do not delete data when object destroyed
+        kCleanupDelete,     // delete with "delete[]"
+    } Cleanup;
+
+    /*
+     * Stuff raw data into the object.  The caller can use the "cleanup"
+     * parameter to decide whether or not the Message object owns the data.
+     */
+    void setRaw(const unsigned char* data, int len, Cleanup cleanup);
+
+    /*
+     * Send a "name=value" pair.
+     */
+    void setConfig(const char* name, const char* value);
+
+    /*
+     * Send a command/arg pair.
+     */
+    void setCommand(int cmd, int arg);
+    void setCommandExt(int cmd, int arg0, int arg1, int arg2);
+
+    /*
+     * Send a multi-part log message.
+     */
+    void setLogBundle(const android_LogBundle* pBundle);
+
+    /*
+     * Simple accessors.
+     */
+    MessageType getType(void) const { return mType; }
+    const unsigned char* getData(void) const { return mData; }
+    int getLength(void) const { return mLength; }
+
+    /*
+     * Not-so-simple accessors.  These coerce the raw data into an object.
+     *
+     * The data returned by these may not outlive the Message, so make
+     * copies if you plan to use them long-term.
+     */
+    bool getConfig(const char** pName, const char** pValue);
+    bool getCommand(int* pCmd, int* pArg);
+    bool getLogBundle(android_LogBundle* pBundle);
+
+    /*
+     * Read or write this message on the specified pipe.
+     *
+     * If "wait" is true, read() blocks until a message arrives.  Only
+     * one thread should be reading at a time.
+     */
+    bool read(Pipe* pPipe, bool wait);
+    bool write(Pipe* pPipe) const;
+
+private:
+    Message& operator=(const Message&);     // not defined
+    Message(const Message&);                // not defined
+
+    void reset(void) {
+        if (mCleanup == kCleanupDelete)
+            delete[] mData;
+
+        mType = kTypeUnknown;
+        mCleanup = kCleanupNoDelete;
+        mData = NULL;
+        mLength = -1;
+    }
+
+    MessageType     mType;
+    Cleanup         mCleanup;
+    unsigned char*  mData;
+    int             mLength;
+    struct iovec    mVec;
+};
+
+
+/*
+ * Abstraction of higher-level communication channel.
+ *
+ * This may be used from multiple threads simultaneously.  Blocking on
+ * the read pipe from multiple threads will have unpredictable behavior.
+ *
+ * Does not take ownership of the pipes passed in to init().
+ */
+class MessageStream {
+public:
+    MessageStream(void)
+        : mReadPipe(NULL), mWritePipe(NULL)
+        {}
+    ~MessageStream(void) {}
+
+    /*
+     * Initialize object and exchange greetings.  "initateHello" determines
+     * whether we send "Hello" or block waiting for it to arrive.  Usually
+     * the "parent" initiates.
+     */
+    bool init(Pipe* readPipe, Pipe* writePipe, bool initiateHello);
+
+    bool isReady(void) const { return mReadPipe != NULL && mWritePipe != NULL; }
+
+    /*
+     * Send a message immediately.
+     */
+    bool send(const Message* pMsg) { return pMsg->write(mWritePipe); }
+
+    /*
+     * Receive a message.
+     */
+    bool recv(Message* pMsg, bool wait) { return pMsg->read(mReadPipe, wait); }
+
+    /*
+     * Close communication pipes.  Further attempts to send or receive
+     * will fail.  Note this doesn't actually "close" the pipes, because
+     * we don't own them.
+     */
+    void close(void) { mReadPipe = mWritePipe = NULL; }
+
+    /*
+     * Get our incoming traffic pipe.  This is useful on Linux systems
+     * because it allows access to the file descriptor which can be used
+     * in a select() call.
+     */
+    Pipe* getReadPipe(void) { return mReadPipe; }
+
+private:
+    enum {
+        kHelloMsg       = 0x4e303047,       // 'N00G'
+        kHelloAckMsg    = 0x31455221,       // '1ER!'
+    };
+
+    /* communication pipes; note we don't own these */
+    Pipe*   mReadPipe;
+    Pipe*   mWritePipe;
+};
+
+}; // namespace android
+
+#endif // _LIBS_UTILS_MESSAGE_STREAM_H
diff --git a/simulator/app/MyApp.cpp b/simulator/app/MyApp.cpp
new file mode 100644
index 0000000..313e44d
--- /dev/null
+++ b/simulator/app/MyApp.cpp
@@ -0,0 +1,547 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Application entry point.
+//
+
+// 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"   // needed for Windows build
+#include "wx/fs_zip.h"
+
+#include "MainFrame.h"
+#include "MyApp.h"
+#include <utils/executablepath.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <signal.h>
+
+#if defined(HAVE_WINDOWS_PATHS)
+# include <windows.h>
+#endif
+
+
+/* the name of our config file */
+static wxString kConfigFileName = wxT(".android.cf");
+
+#ifdef HAVE_WINDOWS_PATHS
+static wxString kExeSuffix = wxT(".exe");
+#else
+static wxString kExeSuffix = wxT("");
+#endif
+
+/* do we want to kill the runtime? */
+bool gWantToKill = false;
+
+/*
+ * Signal handler for Ctrl-C.  Under Linux we seem to get hit twice,
+ * possibly once for each thread.
+ *
+ * Avoid using LOG here -- it's not reentrant.  Actually, just avoid doing
+ * anything here.
+ *
+ * Cygwin will ignore the signal but doesn't seem to call the signal
+ * handler.  MinGW just kills the process.
+ */
+static void SignalHandler(int sigNum)
+{
+    printf("Sim: received signal %d (%s)\n", sigNum,
+        sigNum == SIGINT ? "SIGINT" : "???");
+    gWantToKill = true;
+}
+
+
+/* wxWidgets magic; creates appropriate main entry function */
+IMPLEMENT_APP(MyApp)
+
+/*
+ * Application entry point.
+ */
+bool MyApp::OnInit()
+{
+    static wxString helpFilePath = wxT("simulator/help/unnamed.htb");
+
+    /*
+     * Parse args.
+     */
+
+    SetDefaults();
+    
+    char** cargv = (char**)malloc(argc * sizeof(char*));
+    for (int i=0; i<argc; i++) {
+	wxCharBuffer tmp = wxString(argv[i]).ToAscii();
+        cargv[i] = tmp.release();
+    }
+    if (!ParseArgs(argc, cargv)) {
+	for (int i=0; i<argc; i++)
+	    free(cargv[i]);
+	free(cargv);
+        return FALSE;
+    }
+    for (int i=0; i<argc; i++)
+        free(cargv[i]);
+    free(cargv);
+    
+    if (!ProcessConfigFile())
+        return FALSE;
+
+    /*
+     * (Try to) catch SIGINT (Ctrl-C).
+     */
+    bool trapInt = false;
+    mPrefs.GetBool("trap-sigint", &trapInt);
+    if (trapInt) {
+        printf("Sim: catching SIGINT\n");
+        signal(SIGINT, SignalHandler);
+    }
+
+    signal(SIGPIPE, SIG_IGN);
+
+    /*
+     * Set stdout to unbuffered.  This is needed for MinGW/MSYS.
+     * Set stderr while we're at it.
+     */
+    setvbuf(stdout, NULL, _IONBF, 0);
+    setvbuf(stderr, NULL, _IONBF, 0);
+
+    /*
+     * Initialize asset manager.
+     */
+    mpAssetManager = NULL;
+    printf("Sim: looking in '%s' for my assets\n", (const char*) mSimAssetPath.ToAscii());
+    ChangeAssetDirectory(mSimAssetPath);
+
+    /*
+     * Add JPEG and PNG image handlers.
+     */
+    ::wxInitAllImageHandlers();
+
+    /*
+     * Set up the help file browser.  We're using wxHtmlHelpController
+     * because it seems to be the only "portable" version other than
+     * the "use external browser" version.
+     */
+    wxFileSystem::AddHandler(new wxZipFSHandler);
+    mHelpController = new wxHtmlHelpController;
+
+    wxString helpFileName;
+    helpFileName = mSimAssetPath;
+    helpFileName += '/';
+    helpFileName += helpFilePath;
+    mHelpController->Initialize(helpFileName);
+
+    /*
+     * Create the main window, which just holds some of our UI.
+     */
+    wxPoint pos(wxDefaultPosition);
+    mPrefs.GetInt("window-main-x", &pos.x);
+    mPrefs.GetInt("window-main-y", &pos.y);
+    mpMainFrame = new MainFrame(wxT("Android Simulator"), pos, wxDefaultSize,
+        wxDEFAULT_FRAME_STYLE);
+    mpMainFrame->Show(TRUE);
+    SetTopWindow(mpMainFrame);
+
+    return TRUE;
+}
+
+/*
+ * Change our asset directory.  This requires deleting the existing
+ * AssetManager and creating a new one.  Note that any open Assets will
+ * still be valid.
+ */
+void MyApp::ChangeAssetDirectory(const wxString& dir)
+{
+    delete mpAssetManager;
+    mpAssetManager = new android::AssetManager;
+    android::String8 path(dir.ToAscii());
+    path.appendPath("simulator.zip");
+    mpAssetManager->addAssetPath(path, NULL);
+    // mpAssetManager->setLocale(xxx);
+    mpAssetManager->setVendor("google");
+}
+
+
+/*
+ * App is shutting down.  Save the config file.
+ */
+int MyApp::OnExit(void)
+{
+    if (mPrefs.GetDirty()) {
+        printf("Sim: writing config file to '%s'\n",
+            (const char*) mConfigFile.ToAscii());
+        if (!mPrefs.Save(mConfigFile.ToAscii())) {
+            fprintf(stderr, "Sim: ERROR: prefs save to '%s' failed\n",
+                (const char*) mConfigFile.ToAscii());
+        }
+    }
+
+    return 0;
+}
+
+static ssize_t
+find_last_slash(const wxString& s)
+{
+    int slash = s.Last('/');
+    if (slash < 0) {
+        slash = s.Last('\\');
+    }
+    return slash;
+}
+
+
+/*
+ * Set some default parameters
+ */
+void MyApp::SetDefaults()
+{
+    mDebuggerOption = false;
+
+    /* Get the path to this executable, which should
+     * end in something like "/host/linux-x86/bin/simulator".
+     * (The full path may begin with something like "out"
+     * or "out/debug".)
+     */
+    char exepath[PATH_MAX];
+    executablepath(exepath);
+    wxString out = wxString::FromAscii(exepath);
+
+    /* Get the path to the root host directory;  e.g., "out/host".
+     * We can do this by removing the last three slashes
+     * and everything after/between them ("/linux-x86/bin/simulator").
+     */
+    for (int i = 0; i < 3; i++) {
+        int slash = find_last_slash(out);
+        assert(slash >= 0);
+        out.Truncate(slash);
+    }
+
+    /* Get the location of the assets directory; something like
+     * "out/host/common/sim-assets"
+     */
+    mSimAssetPath = out;
+    mSimAssetPath.Append(wxT("/common/sim-assets"));
+
+    /* Get the location of the simulated device filesystem.
+     * We can't reliably predict this based on the executable
+     * location, so try to get it from the environment.
+     */
+    char *envOut = getenv("ANDROID_PRODUCT_OUT");
+    if (envOut == NULL) {
+        fprintf(stderr,
+                "WARNING: $ANDROID_PRODUCT_OUT not set in environment\n");
+        envOut = "";
+    }
+
+    // the root of the android stuff
+    mAndroidRoot = wxString::FromAscii(envOut);
+    mAndroidRoot.Append(wxT("/system"));
+    
+    // where runtime is
+    mRuntimeExe = mAndroidRoot;
+    mRuntimeExe.Append(wxT("/bin/runtime"));
+    mRuntimeExe.Append(kExeSuffix);
+    
+    printf("mAndroidRoot='%s'\n", (const char*) mAndroidRoot.ToAscii());
+    printf("mSimAssetPath='%s'\n", (const char*) mSimAssetPath.ToAscii());
+}
+
+
+/*
+ * Parse command-line arguments.
+ *
+ * Returns "false" if we have a parsing error.
+ */
+bool MyApp::ParseArgs(int argc, char** argv)
+{
+    int ic;
+
+    opterr = 0;     // don't complain about unrecognized options
+
+    if (false) {
+        printf("MyApp args:\n");
+        for (int i = 0; i < argc; i++)
+            printf("  %2d: '%s'\n", i, (const char*) argv[i]);
+    }
+
+    while (1) {
+        ic = getopt(argc, argv, "tj:da:f:rx:");
+        if (ic < 0)
+            break;
+
+        switch (ic) {
+        case 'j':
+            mAutoRunApp = wxString::FromAscii(optarg);
+            break;
+        case 't':
+            mAutoRunApp = wxT("com.android.testharness.RunAll");
+            break;
+        case 'd':
+            mDebuggerOption = true;
+            break;
+        case 'x':
+            mDebuggerScript = wxString::FromAscii(optarg);
+            mDebuggerOption = true;     // force debug if a script is being used
+            break;
+        case 'a':       // simulator asset dir
+            mSimAssetPath = wxString::FromAscii(optarg);
+            break;
+        case 'f':       // simulator config file
+            mConfigFile = wxString::FromAscii(optarg);
+            break;
+        case 'r':       // reset path-based options to defaults
+            mResetPaths = true;
+            break;
+        default:
+            fprintf(stderr, "WARNING: unknown sim option '%c'\n", ic);
+            break;
+        }
+    }
+
+    return true;
+}
+
+
+/*
+ * Convert a path to absolute form, if needed.
+ *
+ * String manipulation would be more efficient than system calls, but
+ * less reliable.
+ *
+ * We need to use GetCurrentDirectory() under Windows because, under
+ * Cygwin, some wxWidgets features require "C:" paths rather than
+ * local-rooted paths.  Probably needed for stand-alone MinGW too.
+ */
+void MyApp::AbsifyPath(wxString& dir)
+{
+    char oldDir[512], newDir[512];
+    wxString newDirStr;
+
+    // We still need to do this under Cygwin even if the path is
+    // already absolute.
+    //if (dir[0] == '/' || dir[0] == '\\')
+    //    return;
+
+    if (getcwd(oldDir, sizeof(oldDir)) == NULL) {
+        fprintf(stderr, "getcwd() failed\n");
+        return;
+    }
+
+    if (chdir(dir.ToAscii()) == 0) {
+#if defined(HAVE_WINDOWS_PATHS)
+        DWORD dwRet;
+        dwRet = GetCurrentDirectory(sizeof(newDir), newDir);
+        if (dwRet == 0 || dwRet > sizeof(newDir))
+            sprintf(newDir, "GET_DIR_FAILED %lu", dwRet);
+#else
+        if (getcwd(newDir, sizeof(newDir)) == NULL)
+            strcpy(newDir, "GET_DIR_FAILED");
+#endif
+        newDirStr = wxString::FromAscii(newDir);
+        chdir(oldDir);
+    } else {
+        fprintf(stderr, "WARNING: unable to chdir to '%s' from '%s'\n",
+            (const char*) dir.ToAscii(), oldDir);
+        newDirStr = dir;
+    }
+
+    //dir = "c:/dev/cygwin";
+    //dir += newDirStr;
+    dir = newDirStr;
+}
+
+
+/*
+ * Load and process our configuration file.
+ */
+bool MyApp::ProcessConfigFile(void)
+{
+    wxString homeConfig;
+    bool configLoaded = false;
+
+    if (getenv("HOME") != NULL) {
+        homeConfig = wxString::FromAscii(getenv("HOME"));
+        homeConfig += '/';
+        homeConfig += kConfigFileName;
+    } else {
+        homeConfig = wxT("./");
+        homeConfig += kConfigFileName;
+    }
+
+    /*
+     * Part 1: read the config file.
+     */
+
+    if (mConfigFile.Length() > 0) {
+        /*
+         * Read from specified config file.  We absolutify the path
+         * first so that we're guaranteed to be hitting the same file
+         * even if the cwd changes.
+         */
+        if (access(mConfigFile.ToAscii(), R_OK) != 0) {
+            fprintf(stderr, "ERROR: unable to open '%s'\n",
+                (const char*) mConfigFile.ToAscii());
+            return false;
+        }
+        if (!mPrefs.Load(mConfigFile.ToAscii())) {
+            fprintf(stderr, "Failed loading config file '%s'\n",
+                (const char*) mConfigFile.ToAscii());
+            return false;
+        } else {
+            configLoaded = true;
+        }
+    } else {
+        /*
+         * Try ./android.cf, then $HOME/android.cf.  If we find one and
+         * read it successfully, save the name in mConfigFile.
+         */
+        {
+            wxString fileName;
+
+            fileName = wxT(".");
+            AbsifyPath(fileName);
+            fileName += wxT("/");
+            fileName += kConfigFileName;
+
+            if (access(fileName.ToAscii(), R_OK) == 0) {
+                if (mPrefs.Load(fileName.ToAscii())) {
+                    mConfigFile = fileName;
+                    configLoaded = true;
+                } else {
+                    /* damaged config files are always fatal */
+                    fprintf(stderr, "Failed loading config file '%s'\n",
+                        (const char*) fileName.ToAscii());
+                    return false;
+                }
+            }
+        }
+        if (!configLoaded) {
+            if (homeConfig.Length() > 0) {
+                if (access(homeConfig.ToAscii(), R_OK) == 0) {
+                    if (mPrefs.Load(homeConfig.ToAscii())) {
+                        mConfigFile = homeConfig;
+                        configLoaded = true;
+                    } else {
+                        /* damaged config files are always fatal */
+                        fprintf(stderr, "Failed loading config file '%s'\n",
+                            (const char*) homeConfig.ToAscii());
+                        return false;
+                    }
+                }
+            }
+        }
+
+    }
+
+    /* if we couldn't find one to load, create a new one in $HOME */
+    if (!configLoaded) {
+        mConfigFile = homeConfig;
+        if (!mPrefs.Create()) {
+            fprintf(stderr, "prefs creation failed\n");
+            return false;
+        }
+    }
+
+    /*
+     * Part 2: reset some entries if requested.
+     *
+     * If you want to reset local items (like paths to binaries) without
+     * disrupting other options, specifying the "reset" flag will cause
+     * some entries to be removed, and new defaults generated below.
+     */
+
+    if (mResetPaths) {
+        if (mPrefs.RemovePref("debugger"))
+            printf("  removed pref 'debugger'\n");
+        if (mPrefs.RemovePref("valgrinder"))
+            printf("  removed pref 'valgrinder'\n");
+    }
+
+    /*
+     * Find GDB.
+     */
+    if (!mPrefs.Exists("debugger")) {
+        static wxString paths[] = {
+            wxT("/bin"), wxT("/usr/bin"), wxString()
+        };
+        wxString gdbPath;
+
+        FindExe(wxT("gdb"), paths, wxT("/usr/bin/gdb"), &gdbPath);
+        mPrefs.SetString("debugger", gdbPath.ToAscii());
+    }
+
+
+    /*
+     * Find Valgrind.  It currently only exists in Linux, and is installed
+     * in /usr/bin/valgrind by default on our systems.  The default version
+     * is old and sometimes fails, so look for a newer version.
+     */
+    if (!mPrefs.Exists("valgrinder")) {
+        static wxString paths[] = {
+            wxT("/home/fadden/local/bin"), wxT("/usr/bin"), wxString()
+        };
+        wxString valgrindPath;
+
+        FindExe(wxT("valgrind"), paths, wxT("/usr/bin/valgrind"), &valgrindPath);
+        mPrefs.SetString("valgrinder", valgrindPath.ToAscii());
+    }
+
+    /*
+     * Set misc options.
+     */
+    if (!mPrefs.Exists("auto-power-on"))
+        mPrefs.SetBool("auto-power-on", true);
+    if (!mPrefs.Exists("gamma"))
+        mPrefs.SetDouble("gamma", 1.0);
+
+    if (mPrefs.GetDirty()) {
+        printf("Sim: writing config file to '%s'\n",
+            (const char*) mConfigFile.ToAscii());
+        if (!mPrefs.Save(mConfigFile.ToAscii())) {
+            fprintf(stderr, "Sim: ERROR: prefs save to '%s' failed\n",
+                (const char*) mConfigFile.ToAscii());
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Find an executable by searching in several places.
+ */
+/*static*/ void MyApp::FindExe(const wxString& exeName, const wxString paths[],
+    const wxString& defaultPath, wxString* pOut)
+{
+    wxString exePath;
+    wxString slashExe;
+
+    slashExe = wxT("/");
+    slashExe += exeName;
+    slashExe += kExeSuffix;
+
+    while (!(*paths).IsNull()) {
+        wxString tmp;
+
+        tmp = *paths;
+        tmp += slashExe;
+        if (access(tmp.ToAscii(), X_OK) == 0) {
+            printf("Sim: Found '%s' in '%s'\n", (const char*) exeName.ToAscii(), 
+                    (const char*) tmp.ToAscii());
+            *pOut = tmp;
+            return;
+        }
+
+        paths++;
+    }
+
+    printf("Sim: Couldn't find '%s', defaulting to '%s'\n",
+        (const char*) exeName.ToAscii(), (const char*) defaultPath.ToAscii());
+    *pOut = defaultPath;
+}
+
diff --git a/simulator/app/MyApp.h b/simulator/app/MyApp.h
new file mode 100644
index 0000000..d18368d
--- /dev/null
+++ b/simulator/app/MyApp.h
@@ -0,0 +1,93 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Application class.
+//
+#ifndef _SIM_APPMAIN_H
+#define _SIM_APPMAIN_H
+
+#include "wx/help.h"
+#include "wx/html/helpctrl.h"
+
+#include "MainFrame.h"
+#include "DeviceManager.h"
+#include "Preferences.h"
+
+#include <utils/AssetManager.h>
+
+/* flag set from signal handler */
+extern bool gWantToKill;
+
+/*
+ * Class representing the application.
+ */
+class MyApp : public wxApp {
+public:
+    MyApp(void)
+        : mHelpController(NULL), mpMainFrame(NULL), mpAssetManager(NULL),
+          mResetPaths(false)        // configurable; reset prefs with paths
+        {}
+    ~MyApp(void) 
+    { 
+        delete mpAssetManager;
+        delete mHelpController; 
+    }
+
+    virtual bool OnInit(void);
+    virtual int OnExit(void);
+
+    wxHtmlHelpController* GetHelpController(void) const {
+        return mHelpController;
+    }
+
+    Preferences* GetPrefs(void)                 { return &mPrefs; }
+
+    /* return a pointer to the main window */
+    wxWindow* GetMainFrame(void) { return mpMainFrame; }
+
+    /* get a pointer to our Asset Manager */
+    android::AssetManager* GetAssetManager(void) { return mpAssetManager; }
+
+    /* change the asset dir; requires re-creating Asset Manager */
+    void ChangeAssetDirectory(const wxString& dir);
+
+    const wxString& GetConfigFileName(void) const { return mConfigFile; }
+
+    wxString GetSimAssetPath()                  { return mSimAssetPath; }
+    wxString GetAndroidRoot()                   { return mAndroidRoot; }
+    wxString GetRuntimeExe()                    { return mRuntimeExe; }
+    bool GetDebuggerOption()                    { return mDebuggerOption; }
+    wxString GetDebuggerScript()                { return mDebuggerScript; }
+    wxString GetAutoRunApp()                    { return mAutoRunApp; }
+
+    void Vibrate(int vibrateOn)                 { ((MainFrame*)mpMainFrame)->Vibrate(vibrateOn); }
+
+private:
+    void SetDefaults();
+    bool ParseArgs(int argc, char** argv);
+    void AbsifyPath(wxString& dir);
+    bool ProcessConfigFile(void);
+    static void FindExe(const wxString& exeName, const wxString paths[],
+        const wxString& defaultPath, wxString* pOut);
+
+    wxHtmlHelpController*   mHelpController;
+
+    wxWindow*       mpMainFrame;
+
+    android::AssetManager*  mpAssetManager;
+
+    wxString        mAndroidRoot;
+    wxString        mSimAssetPath;
+    wxString        mRuntimeExe;
+
+    /* command-line options */
+    wxString        mConfigFile;
+    bool            mResetPaths;
+    bool            mDebuggerOption;
+	wxString		mDebuggerScript;
+    wxString        mAutoRunApp;
+
+    Preferences     mPrefs;
+};
+
+#endif // _SIM_APPMAIN_H
diff --git a/simulator/app/PhoneButton.cpp b/simulator/app/PhoneButton.cpp
new file mode 100644
index 0000000..eca7ddc
--- /dev/null
+++ b/simulator/app/PhoneButton.cpp
@@ -0,0 +1,180 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Simulated device data.
+//
+
+// 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"   // needed for Windows build
+
+#include "LinuxKeys.h"
+#include "PhoneButton.h"
+
+using namespace android;
+
+
+/*
+ * Create a PhoneButton without a backing image.
+ */
+bool PhoneButton::Create(const char* label)
+{
+    assert(!mHasImage);     // quick check for re-use
+
+    mKeyCode = LookupKeyCode(label);
+    if (mKeyCode == kKeyCodeUnknown) {
+        fprintf(stderr, "WARNING: key code '%s' not recognized\n", label);
+        // keep going
+    }
+
+    return true;
+}
+
+/*
+ * Create a PhoneButton with an associated image.  Don't load the image yet.
+ */
+bool PhoneButton::Create(const char* label, const char* imageFileName,
+    int x, int y)
+{
+    if (!Create(label))
+        return false;
+
+    if (mSelectedImage.Create(imageFileName, x, y))
+        mHasImage = true;
+    else
+        fprintf(stderr, "Warning: image create (%s, %d, %d) failed\n",
+            imageFileName, x, y);
+
+    return true;
+}
+
+/*
+ * Load the image, if any.
+ */
+bool PhoneButton::LoadResources(void)
+{
+    if (!mHasImage)
+        return true;        // no image associated with this button
+
+    bool result = mSelectedImage.LoadResources();
+    if (result)
+        CreateHighlightedBitmap();
+    return result;
+}
+
+/*
+ * Unload the image if we loaded one.
+ */
+bool PhoneButton::UnloadResources(void)
+{
+    if (!mHasImage)
+        return true;
+
+    return mSelectedImage.UnloadResources();
+}
+
+/* use an inline instead of macro so we don't evaluate args multiple times */
+static inline int MinVal(int a, int b) { return (a < b ? a : b); }
+
+/*
+ * Create the "highlighted" bitmap from the "selected" image.
+ */
+void PhoneButton::CreateHighlightedBitmap(void)
+{
+    wxBitmap* src = mSelectedImage.GetBitmap();
+    assert(src != NULL);
+    wxImage tmpImage = src->ConvertToImage();
+
+    unsigned char* pRGB = tmpImage.GetData();       // top-left RGBRGB...
+    int x, y;
+
+    /*
+     * Modify the color used for the "highlight" image.
+     */
+    for (y = tmpImage.GetHeight()-1; y >= 0; --y) {
+        for (x = tmpImage.GetWidth()-1; x >= 0; --x) {
+            *(pRGB)   = MinVal(*(pRGB)   + *(pRGB) / 8, 255);
+            *(pRGB+1) = MinVal(*(pRGB+1) + *(pRGB+1) / 8, 255);
+            *(pRGB+2) = *(pRGB+2) * 5 / 8;
+
+            pRGB += 3;
+        }
+    }
+
+    mHighlightedBitmap = wxBitmap(tmpImage);
+}
+
+/*
+ * Check to see if the button "collides" with the specified point.
+ *
+ * This is currently a simple rectangle check, but could be modified
+ * to take image transparency into account.
+ */
+bool PhoneButton::CheckCollision(int x, int y) const
+{
+    if (!mHasImage)
+        return false;
+
+    return (x >= mSelectedImage.GetX() &&
+            x < mSelectedImage.GetX() + mSelectedImage.GetWidth() &&
+            y >= mSelectedImage.GetY() &&
+            y < mSelectedImage.GetY() + mSelectedImage.GetHeight());
+}
+
+/*
+ * Look up a key code based on a string.
+ *
+ * Returns kKeyCodeUnknown if the label doesn't match anything.
+ */
+KeyCode PhoneButton::LookupKeyCode(const char* label) const
+{
+    static const struct {
+        const char* label;
+        int keyCode;
+    } codeList[] = {
+        { "soft-left",      KEY_MENU },
+        { "soft-right",     KEY_KBDILLUMUP },
+        { "home",           KEY_HOME },
+        { "back",           KEY_BACK },
+        { "call",           KEY_F3 },
+        { "phone-dial",     KEY_F3 },
+        { "end-call",       KEY_F4 },
+        { "phone-hangup",   KEY_F4 },
+        { "0",              KEY_0 },
+        { "1",              KEY_1 },
+        { "2",              KEY_2 },
+        { "3",              KEY_3 },
+        { "4",              KEY_4 },
+        { "5",              KEY_5 },
+        { "6",              KEY_6 },
+        { "7",              KEY_7 },
+        { "8",              KEY_8 },
+        { "9",              KEY_9 },
+        { "star",           KEY_SWITCHVIDEOMODE },
+        { "pound",          KEY_KBDILLUMTOGGLE },
+        { "dpad-up",        KEY_UP },
+        { "dpad-down",      KEY_DOWN },
+        { "dpad-left",      KEY_LEFT },
+        { "dpad-right",     KEY_RIGHT },
+        { "dpad-center",    KEY_REPLY },
+        { "volume-up",      KEY_VOLUMEUP },
+        { "volume-down",    KEY_VOLUMEDOWN },
+        { "power",          KEY_POWER },
+        { "camera",         KEY_CAMERA },
+        //{ "clear",          kKeyCodeClear },
+    };
+    const int numCodes = sizeof(codeList) / sizeof(codeList[0]);
+
+    for (int i = 0; i < numCodes; i++) {
+        if (strcmp(label, codeList[i].label) == 0)
+            return (KeyCode) codeList[i].keyCode;
+    }
+
+    return kKeyCodeUnknown;
+};
+
diff --git a/simulator/app/PhoneButton.h b/simulator/app/PhoneButton.h
new file mode 100644
index 0000000..4bbaf56
--- /dev/null
+++ b/simulator/app/PhoneButton.h
@@ -0,0 +1,80 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Phone button image holder.
+//
+#ifndef _SIM_PHONE_BUTTON_H
+#define _SIM_PHONE_BUTTON_H
+
+#include "LoadableImage.h"
+#include <ui/KeycodeLabels.h>
+
+/*
+ * One button on a phone.  Position, size, and a highlight graphic.  The
+ * coordinates are relative to the device graphic.
+ *
+ * We now have a "highlighted" graphic for mouse-overs and a "selected"
+ * graphic for button presses.  We assume they have the same dimensions.
+ * We currently assume that either both or neither exist, because we
+ * generate one from the other.
+ */
+class PhoneButton {
+public:
+    PhoneButton(void)
+        : mHasImage(false), mKeyCode(kKeyCodeUnknown)
+        {}
+    virtual ~PhoneButton(void) {}
+    PhoneButton(const PhoneButton& src)
+        : mHasImage(false), mKeyCode(kKeyCodeUnknown)
+    {
+        CopyMembers(src);
+    }
+    PhoneButton& operator=(const PhoneButton& src) {
+        if (this != &src) {
+            // Unload any resources in case we're using operator= to
+            // assign to an existing object.
+            mSelectedImage.UnloadResources();
+            // Copy fields.
+            CopyMembers(src);
+        }
+        return *this;
+    }
+    void CopyMembers(const PhoneButton& src) {
+        mSelectedImage = src.mSelectedImage;
+        mHighlightedBitmap = src.mHighlightedBitmap;
+        mHasImage = src.mHasImage;
+        mKeyCode = src.mKeyCode;
+    }
+
+    /* finish construction of PhoneButton, with or without an image */
+    bool Create(const char* label);
+    bool Create(const char* label, const char* imageFileName, int x, int y);
+
+    int GetX(void) const { return mSelectedImage.GetX(); }
+    int GetY(void) const { return mSelectedImage.GetY(); }
+    int GetWidth(void) const { return mSelectedImage.GetWidth(); }
+    int GetHeight(void) const { return mSelectedImage.GetHeight(); }
+    wxBitmap* GetHighlightedBitmap(void) { return &mHighlightedBitmap; }
+    wxBitmap* GetSelectedBitmap(void) const {
+        return mSelectedImage.GetBitmap();
+    }
+
+    bool CheckCollision(int x, int y) const;
+    KeyCode GetKeyCode(void) const { return mKeyCode; }
+
+    // load or unload the image bitmap, if any
+    bool LoadResources(void);
+    bool UnloadResources(void);
+
+private:
+    void CreateHighlightedBitmap(void);
+    KeyCode LookupKeyCode(const char* label) const;
+
+    LoadableImage       mSelectedImage;
+    wxBitmap            mHighlightedBitmap;
+    bool                mHasImage;          // both exist or neither exist
+
+    KeyCode    mKeyCode;
+};
+
+#endif // _SIM_PHONE_BUTTON_H
diff --git a/simulator/app/PhoneCollection.cpp b/simulator/app/PhoneCollection.cpp
new file mode 100644
index 0000000..5cddfa8
--- /dev/null
+++ b/simulator/app/PhoneCollection.cpp
@@ -0,0 +1,174 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Our collection of devices.
+//
+
+// 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"   // needed for Windows build
+
+
+#include "PhoneCollection.h"
+#include "PhoneData.h"
+#include "MyApp.h"
+
+#include <utils.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <assert.h>
+
+using namespace android;
+
+/*static*/ PhoneCollection* PhoneCollection::mpInstance = NULL;
+
+/*static*/ const char* PhoneCollection::kLayoutFile = "layout.xml";
+
+
+/*
+ * (Re-)scan the specified directory for phones.  We register a hit if we can
+ * see a file called "<directory>/layout.xml".
+ */
+void PhoneCollection::ScanForPhones(const char* directory)
+{
+    /*
+     * Scan through the directory and find everything that looks like it
+     * might hold phone data.
+     */
+    StringArray strArr;
+
+#ifdef BEFORE_ASSET
+    DIR* dirp;
+    struct dirent* entp;
+
+    dirp = opendir(directory);
+    if (dirp == NULL) {
+        char buf[512];
+        fprintf(stderr, "ERROR: unable to scan directory '%s' for phone data\n",
+            directory);
+        fprintf(stderr, "Current dir is %s\n", getcwd(buf, sizeof(buf)));
+        return;
+    }
+
+    while (1) {
+        wxString dirName;
+        wxString fileName;
+
+        entp = readdir(dirp);
+        if (entp == NULL)
+            break;              // done with scan
+        dirName = directory;
+        dirName += '/';
+        dirName += entp->d_name;
+        fileName = dirName;
+        fileName += '/';
+        fileName += kLayoutFile;
+
+        if (access(fileName, R_OK) == 0) {
+            strArr.push_back(dirName);
+            //printf("--- examining '%s'\n", (const char*) fileName);
+        }
+    }
+    closedir(dirp);
+#else
+    android::AssetManager* pAssetMgr = ((MyApp*)wxTheApp)->GetAssetManager();
+    android::AssetDir* pDir;
+    int i, count;
+
+    pDir = pAssetMgr->openDir("");
+    assert(pDir != NULL);
+    count = pDir->getFileCount();
+
+    for (i = 0; i < count; i++) {
+        android::String8 layoutPath;
+
+        if (pDir->getFileType(i) != kFileTypeDirectory)
+            continue;
+
+        layoutPath = pDir->getFileName(i);
+        layoutPath.appendPath(kLayoutFile);
+
+        if (pAssetMgr->getFileType(layoutPath.string()) == kFileTypeRegular) {
+            strArr.push_back(pDir->getFileName(i).string());
+            printf("--- examining '%s'\n", layoutPath.string());
+        }
+    }
+
+    delete pDir;
+#endif
+
+    if (strArr.size() == 0) {
+        fprintf(stderr, "ERROR: no phone data found in '%s'\n", directory);
+        return;
+    }
+
+    /*
+     * Found some candidates.  If they parse successfully, add them to
+     * our list.
+     *
+     * We sort them first, because it's nice when everybody's user
+     * interface looks the same.  Note we're sorting the directory name,
+     * so it's possible to define a sort order in the filesystem that
+     * doesn't require messing up the phone's title string.
+     */
+    mPhoneList.clear();
+    strArr.sort(StringArray::cmpAscendingAlpha);
+
+    for (int i = 0; i < strArr.size(); i++) {
+        PhoneData tmpPhone;
+
+        if (!tmpPhone.Create(strArr.getEntry(i))) {
+            fprintf(stderr, "Sim: Abandoning phone '%s'\n", strArr.getEntry(i));
+            //strArr.erase(i);
+            //i--;
+        } else {
+            if (GetPhoneData(tmpPhone.GetName()) != NULL) {
+                fprintf(stderr, "Sim: ERROR: duplicate name '%s' in '%s'\n",
+                    tmpPhone.GetName(), strArr.getEntry(i));
+            } else {
+                mPhoneList.push_back(tmpPhone);
+            }
+        }
+    }
+}
+
+
+/*
+ * Return the Nth member of the phone data array.  (Replace w/Vector.)
+ */
+PhoneData* PhoneCollection::GetPhoneData(int idx)
+{
+    typedef List<PhoneData>::iterator Iter;
+
+    for (Iter ii = mPhoneList.begin(); ii != mPhoneList.end(); ++ii) {
+        if (idx == 0)
+            return &(*ii);
+        --idx;
+    }
+    return NULL;
+}
+
+/*
+ * Return the entry whose phone data name matches "name".
+ */
+PhoneData* PhoneCollection::GetPhoneData(const char* name)
+{
+    typedef List<PhoneData>::iterator Iter;
+
+    for (Iter ii = mPhoneList.begin(); ii != mPhoneList.end(); ++ii) {
+        if (strcasecmp((*ii).GetName(), name) == 0)
+            return &(*ii);
+    }
+    return NULL;
+}
+
diff --git a/simulator/app/PhoneCollection.h b/simulator/app/PhoneCollection.h
new file mode 100644
index 0000000..d16c4a0
--- /dev/null
+++ b/simulator/app/PhoneCollection.h
@@ -0,0 +1,52 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Our collection of devices.
+//
+#ifndef _SIM_PHONE_COLLECTION_H
+#define _SIM_PHONE_COLLECTION_H
+
+#include <stdlib.h>
+#include "PhoneData.h"
+
+/*
+ * Only one instance of this class exists.  It contains a list of all
+ * known devices, and methods for scanning for devices.
+ */
+class PhoneCollection {
+public:
+    /* get the global instance */
+    static PhoneCollection* GetInstance(void) {
+        if (mpInstance == NULL)
+            mpInstance = new PhoneCollection;
+        return mpInstance;
+    }
+    /* destroy the global instance when shutting down */
+    static void DestroyInstance(void) {
+        delete mpInstance;
+        mpInstance = NULL;
+    }
+
+    /* scan for phones in subdirectories of "directory" */
+    void ScanForPhones(const char* directory);
+
+    /* get phone data */
+    int GetPhoneCount(void) const { return mPhoneList.size(); } // slow
+    PhoneData* GetPhoneData(int idx);
+    PhoneData* GetPhoneData(const char* name);
+
+    /* layout.xml filename -- a string constant used in various places */
+    static const char* kLayoutFile;
+
+private:
+    PhoneCollection(void) {}
+    ~PhoneCollection(void) {}
+
+    /* the phone data; make this a Vector someday */
+    android::List<PhoneData>    mPhoneList;
+
+    /* storage for global instance pointer */
+    static PhoneCollection* mpInstance;
+};
+
+#endif // _SIM_PHONE_COLLECTION_H
diff --git a/simulator/app/PhoneData.cpp b/simulator/app/PhoneData.cpp
new file mode 100644
index 0000000..48614fd
--- /dev/null
+++ b/simulator/app/PhoneData.cpp
@@ -0,0 +1,868 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Simulated device data.
+//
+
+// 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"   // needed for Windows build
+
+
+#include "PhoneData.h"
+#include "PhoneButton.h"
+#include "PhoneCollection.h"
+#include "MyApp.h"
+
+#include <utils.h>
+#include <utils/AssetManager.h>
+#include <utils/String8.h>
+
+#include "tinyxml.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+using namespace android;
+
+/* image relative path hack */
+static const char* kRelPathMagic = "::/";
+
+
+/*
+ * ===========================================================================
+ *      PhoneKeyboard
+ * ===========================================================================
+ */
+
+/*
+ * Load a <keyboard> chunk.
+ */
+bool PhoneKeyboard::ProcessAndValidate(TiXmlNode* pNode)
+{
+    //TiXmlNode* pChild;
+    TiXmlElement* pElem;
+    int qwerty = 0;
+    
+    assert(pNode->Type() == TiXmlNode::ELEMENT);
+
+    pElem = pNode->ToElement();
+    pElem->Attribute("qwerty", &qwerty);
+    const char *kmap = pElem->Attribute("keycharmap");
+
+    if (qwerty == 1) {
+        printf("############## PhoneKeyboard::ProcessAndValidate: qwerty = true!\n");
+        mQwerty = true;
+    }
+
+    if (kmap != NULL) {
+        printf("############## PhoneKeyboard::ProcessAndValidate: keycharmap = %s\n", kmap);
+        mKeyMap = strdup(kmap);
+    }
+    
+    return true;
+}
+
+
+/*
+ * ===========================================================================
+ *      PhoneDisplay
+ * ===========================================================================
+ */
+
+/*
+ * Load a <display> chunk.
+ */
+bool PhoneDisplay::ProcessAndValidate(TiXmlNode* pNode)
+{
+    //TiXmlNode* pChild;
+    TiXmlElement* pElem;
+    const char* name;
+    const char* format;
+
+    assert(pNode->Type() == TiXmlNode::ELEMENT);
+
+    /*
+     * Process attributes.  Right now they're all mandatory, but some of
+     * them could be defaulted (e.g. "rotate").
+     *
+     * [We should do some range-checking here.]
+     */
+    pElem = pNode->ToElement();
+    name = pElem->Attribute("name");
+    if (name == NULL)
+        goto missing;
+    if (pElem->Attribute("width", &mWidth) == NULL)
+        goto missing;
+    if (pElem->Attribute("height", &mHeight) == NULL)
+        goto missing;
+    if (pElem->Attribute("refresh", &mRefresh) == NULL)
+        goto missing;
+    format = pElem->Attribute("format");
+    if (format == NULL)
+        goto missing;
+
+    delete[] mName;
+    mName = strdupNew(name);
+
+    if (strcasecmp(format, "rgb565") == 0) {
+        mFormat = android::PIXEL_FORMAT_RGB_565;
+    } else {
+        fprintf(stderr, "SimCFG: unexpected value for display format\n");
+        return false;
+    }
+
+    return true;
+
+missing:
+    fprintf(stderr,
+        "SimCFG: <display> requires name/width/height/format/refresh\n");
+    return false;
+}
+
+
+/*
+ * Returns "true" if the two displays are compatible, "false" if not.
+ *
+ * Compatibility means they have the same resolution, format, refresh
+ * rate, and so on.  Anything transmitted to the runtime as part of the
+ * initial configuration setup should be tested.
+ */
+/*static*/ bool PhoneDisplay::IsCompatible(PhoneDisplay* pDisplay1,
+    PhoneDisplay* pDisplay2)
+{
+    return (pDisplay1->mWidth == pDisplay2->mWidth &&
+            pDisplay1->mHeight == pDisplay2->mHeight &&
+            pDisplay1->mFormat == pDisplay2->mFormat &&
+            pDisplay1->mRefresh == pDisplay2->mRefresh);
+}
+
+
+/*
+ * ===========================================================================
+ *      PhoneView
+ * ===========================================================================
+ */
+
+/*
+ * Load a <view> chunk.
+ */
+bool PhoneView::ProcessAndValidate(TiXmlNode* pNode, const char* directory)
+{
+    TiXmlNode* pChild;
+    TiXmlElement* pElem;
+    int rotate;
+    const char* displayName;
+
+    assert(pNode->Type() == TiXmlNode::ELEMENT);
+
+    /*
+     * Process attributes.  Right now they're all mandatory, but some of
+     * them could be defaulted (e.g. "rotate").
+     *
+     * [We should do some range-checking here.]
+     */
+    pElem = pNode->ToElement();
+    displayName = pElem->Attribute("display");
+    if (displayName == NULL)
+        goto missing;
+    if (pElem->Attribute("x", &mXOffset) == NULL)
+        goto missing;
+    if (pElem->Attribute("y", &mYOffset) == NULL)
+        goto missing;
+    if (pElem->Attribute("rotate", &rotate) == NULL)
+        goto missing;
+
+    switch (rotate) {
+    case 0:     mRotation = kRot0;      break;
+    case 90:    mRotation = kRot90;     break;
+    case 180:   mRotation = kRot180;    break;
+    case 270:   mRotation = kRot270;    break;
+    default:
+                fprintf(stderr, "SimCFG: unexpected value for rotation\n");
+                mRotation = kRotUnknown;
+                return false;
+    }
+
+    delete[] mDisplayName;
+    mDisplayName = android::strdupNew(displayName);
+
+    /*
+     * Process elements.
+     */
+    for (pChild = pNode->FirstChild(); pChild != NULL;
+        pChild = pChild->NextSibling())
+    {
+        if (pChild->Type() == TiXmlNode::COMMENT)
+            continue;
+
+        if (pChild->Type() == TiXmlNode::ELEMENT) {
+            if (strcasecmp(pChild->Value(), "image") == 0) {
+                if (!ProcessImage(pChild, directory))
+                    return false;
+            } else if (strcasecmp(pChild->Value(), "button") == 0) {
+                if (!ProcessButton(pChild, directory))
+                    return false;
+            } else {
+                fprintf(stderr,
+                    "SimCFG: Warning: unexpected elements in <display>\n");
+            }
+        } else {
+            fprintf(stderr, "SimCFG: Warning: unexpected stuff in <display>\n");
+        }
+    }
+
+    return true;
+
+missing:
+    fprintf(stderr,
+        "SimCFG: <view> requires display/x/y/rotate\n");
+    return false;
+}
+
+/*
+ * Handle <image src="zzz" x="123" y="123"/>.
+ */
+bool PhoneView::ProcessImage(TiXmlNode* pNode, const char* directory)
+{
+    TiXmlNode* pChild;
+    TiXmlElement* pElem;
+    int x, y;
+    const char* src;
+    LoadableImage tmpLimg;
+    android::String8 fileName;
+
+    pChild = pNode->FirstChild();
+    if (pChild != NULL) {
+        fprintf(stderr, "SimCFG: <image> is funky\n");
+        return false;
+    }
+
+    /*
+     * All attributes are mandatory.
+     */
+    pElem = pNode->ToElement();
+    src = pElem->Attribute("src");
+    if (src == NULL)
+        goto missing;
+    if (pElem->Attribute("x", &x) == NULL)
+        goto missing;
+    if (pElem->Attribute("y", &y) == NULL)
+        goto missing;
+
+    if (strncmp(src, kRelPathMagic, strlen(kRelPathMagic)) == 0) {
+        fileName = src + strlen(kRelPathMagic);
+    } else {
+        fileName = directory;
+        fileName += "/";
+        fileName += src;
+    }
+
+    tmpLimg.Create(fileName, x, y);
+    mImageList.push_back(tmpLimg);
+
+    return true;
+
+missing:
+    fprintf(stderr, "SimCFG: <image> requires src/x/y\n");
+    return false;
+}
+
+/*
+ * Handle <button keyCode="zzz" src="zzz" x="123" y="123"/> and
+ * <button keyCode="zzz"/>.
+ */
+bool PhoneView::ProcessButton(TiXmlNode* pNode, const char* directory)
+{
+    TiXmlNode* pChild;
+    TiXmlElement* pElem;
+    int x, y;
+    const char* keyCode;
+    const char* src;
+    PhoneButton tmpButton;
+    android::String8 fileName;
+
+    pChild = pNode->FirstChild();
+    if (pChild != NULL) {
+        fprintf(stderr, "SimCFG: button is funky\n");
+        return false;
+    }
+
+    /*
+     * Only keyCode is mandatory.  If they specify "src", then "x" and "y"
+     * are also required.
+     */
+    pElem = pNode->ToElement();
+    keyCode = pElem->Attribute("keyCode");
+    if (keyCode == NULL)
+        goto missing;
+
+    src = pElem->Attribute("src");
+    if (src != NULL) {
+        if (pElem->Attribute("x", &x) == NULL)
+            goto missing;
+        if (pElem->Attribute("y", &y) == NULL)
+            goto missing;
+    }
+
+    if (src == NULL)
+        tmpButton.Create(keyCode);
+    else {
+        if (strncmp(src, kRelPathMagic, strlen(kRelPathMagic)) == 0) {
+            fileName = src + strlen(kRelPathMagic);
+        } else {
+            fileName = directory;
+            fileName += "/";
+            fileName += src;
+        }
+        tmpButton.Create(keyCode, fileName, x, y);
+    }
+
+    mButtonList.push_back(tmpButton);
+
+    return true;
+
+missing:
+    fprintf(stderr, "SimCFG: <button> requires keycode and may have src/x/y\n");
+    return false;
+}
+
+
+/*
+ * Load all resources associated with the display.
+ */
+bool PhoneView::LoadResources(void)
+{
+    typedef List<LoadableImage>::iterator LIter;
+    typedef List<PhoneButton>::iterator BIter;
+
+    for (LIter ii = mImageList.begin(); ii != mImageList.end(); ++ii)
+        (*ii).LoadResources();
+    for (BIter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii)
+        (*ii).LoadResources();
+    return true;
+}
+
+/*
+ * Unload all resources associated with the display.
+ */
+bool PhoneView::UnloadResources(void)
+{
+    typedef List<LoadableImage>::iterator LIter;
+    typedef List<PhoneButton>::iterator BIter;
+
+    for (LIter ii = mImageList.begin(); ii != mImageList.end(); ++ii)
+        (*ii).UnloadResources();
+    for (BIter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii)
+        (*ii).UnloadResources();
+    return true;
+}
+
+
+/*
+ * Get the #of images.
+ */
+int PhoneView::GetBkgImageCount(void) const
+{
+    return mImageList.size();
+}
+
+/*
+ * Return the Nth entry.
+ */
+const LoadableImage* PhoneView::GetBkgImage(int idx) const
+{
+    typedef List<LoadableImage>::const_iterator Iter;
+
+    for (Iter ii = mImageList.begin(); ii != mImageList.end(); ++ii) {
+        if (!idx)
+            return &(*ii);
+        --idx;
+    }
+
+    return NULL;
+}
+
+
+/*
+ * Find the first button that covers the specified coordinates.
+ *
+ * The coordinates must be relative to the upper left corner of the
+ * phone image.
+ */
+PhoneButton* PhoneView::FindButtonHit(int x, int y)
+{
+    typedef List<PhoneButton>::iterator Iter;
+
+    for (Iter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii) {
+        if ((*ii).CheckCollision(x, y))
+            return &(*ii);
+    }
+
+    return NULL;
+}
+
+/*
+ * Find the first button with a matching key code.
+ */
+PhoneButton* PhoneView::FindButtonByKey(KeyCode keyCode)
+{
+    typedef List<PhoneButton>::iterator Iter;
+
+    for (Iter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii) {
+        if ((*ii).GetKeyCode() == keyCode)
+            return &(*ii);
+    }
+
+    return NULL;
+}
+
+
+/*
+ * ===========================================================================
+ *      PhoneMode
+ * ===========================================================================
+ */
+
+/*
+ * Process a <mode name="zzz"> chunk.
+ */
+bool PhoneMode::ProcessAndValidate(TiXmlNode* pNode, const char* directory)
+{
+    TiXmlNode* pChild;
+    const char* name;
+
+    assert(pNode->Type() == TiXmlNode::ELEMENT);
+
+    name = pNode->ToElement()->Attribute("name");
+    if (name == NULL) {
+        fprintf(stderr, "SimCFG: <mode> requires name attrib\n");
+        return false;
+    }
+    SetName(name);
+
+    for (pChild = pNode->FirstChild(); pChild != NULL;
+        pChild = pChild->NextSibling())
+    {
+        if (pChild->Type() == TiXmlNode::COMMENT)
+            continue;
+
+        if (pChild->Type() == TiXmlNode::ELEMENT &&
+            strcasecmp(pChild->Value(), "view") == 0)
+        {
+            PhoneView tmpDisplay;
+            bool result;
+
+            result = tmpDisplay.ProcessAndValidate(pChild, directory);
+            if (!result)
+                return false;
+
+            mViewList.push_back(tmpDisplay);
+        } else {
+            fprintf(stderr, "SimCFG: Warning: unexpected stuff in <mode>\n");
+        }
+    }
+
+    if (mViewList.size() == 0) {
+        fprintf(stderr, "SimCFG: no <view> entries found\n");
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Load all resources associated with the phone.
+ */
+bool PhoneMode::LoadResources(void)
+{
+    typedef List<PhoneView>::iterator Iter;
+
+    for (Iter ii = mViewList.begin(); ii != mViewList.end(); ++ii)
+        (*ii).LoadResources();
+    return true;
+}
+
+/*
+ * Unload all resources associated with the phone.
+ */
+bool PhoneMode::UnloadResources(void)
+{
+    typedef List<PhoneView>::iterator Iter;
+
+    for (Iter ii = mViewList.begin(); ii != mViewList.end(); ++ii)
+        (*ii).UnloadResources();
+    return true;
+}
+
+
+/*
+ * Return the Nth entry.  [make this a Vector?]
+ */
+PhoneView* PhoneMode::GetPhoneView(int viewNum)
+{
+    typedef List<PhoneView>::iterator Iter;
+
+    for (Iter ii = mViewList.begin(); ii != mViewList.end(); ++ii) {
+        if (viewNum == 0)
+            return &(*ii);
+        --viewNum;
+    }
+    return NULL;
+}
+
+
+/*
+ * ===========================================================================
+ *      PhoneData
+ * ===========================================================================
+ */
+
+
+/*
+ * Look for a "layout.xml" in the specified directory.  If found, parse
+ * the contents out.
+ *
+ * Returns "true" on success, "false" on failure.
+ */
+bool PhoneData::Create(const char* directory)
+{
+    android::String8 fileName;
+
+    SetDirectory(directory);
+
+    fileName = directory;
+    fileName += "/";
+    fileName += PhoneCollection::kLayoutFile;
+
+#ifdef BEFORE_ASSET
+    TiXmlDocument doc(fileName);
+    if (!doc.LoadFile())
+#else
+    android::AssetManager* pAssetMgr = ((MyApp*)wxTheApp)->GetAssetManager();
+    TiXmlDocument doc;
+    android::Asset* pAsset;
+    bool result;
+
+    pAsset = pAssetMgr->open(fileName, Asset::ACCESS_STREAMING);
+    if (pAsset == NULL) {
+        fprintf(stderr, "Unable to open asset '%s'\n", (const char*) fileName);
+        return false;
+    } else {
+        //printf("--- opened asset '%s'\n",
+        //    (const char*) pAsset->getAssetSource());
+    }
+
+    /* TinyXml insists that the buffer be NULL-terminated... ugh */
+    char* buf = new char[pAsset->getLength() +1];
+    pAsset->read(buf, pAsset->getLength());
+    buf[pAsset->getLength()] = '\0';
+
+    delete pAsset;
+    result = doc.Parse(buf);
+    delete[] buf;
+
+    if (!result)
+#endif
+    {
+        fprintf(stderr, "SimCFG: ERROR: failed parsing '%s'\n",
+            (const char*) fileName);
+        if (doc.ErrorRow() != 0)
+            fprintf(stderr, "    XML: %s (row=%d col=%d)\n",
+                doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol());
+        else
+            fprintf(stderr, "    XML: %s\n", doc.ErrorDesc());
+        return false;
+    }
+
+    if (!ProcessAndValidate(&doc)) {
+        fprintf(stderr, "SimCFG: ERROR: failed analyzing '%s'\n",
+            (const char*) fileName);
+        return false;
+    }
+
+    printf("SimCFG: loaded data from '%s'\n", (const char*) fileName);
+
+    return true;
+}
+
+/*
+ * TinyXml has loaded and parsed the XML document for us.  We need to
+ * run through the DOM tree, pull out the interesting bits, and make
+ * sure the stuff we need is present.
+ *
+ * Returns "true" on success, "false" on failure.
+ */
+bool PhoneData::ProcessAndValidate(TiXmlDocument* pDoc)
+{
+    bool deviceFound = false;
+    TiXmlNode* pChild;
+
+    assert(pDoc->Type() == TiXmlNode::DOCUMENT);
+
+    for (pChild = pDoc->FirstChild(); pChild != NULL;
+        pChild = pChild->NextSibling())
+    {
+        /*
+         * Find the <device> entry.  There should be exactly one.
+         */
+        if (pChild->Type() == TiXmlNode::ELEMENT) {
+            if (strcasecmp(pChild->Value(), "device") != 0) {
+                fprintf(stderr,
+                    "SimCFG: Warning: unexpected element '%s' at top level\n",
+                    pChild->Value());
+                continue;
+            }
+            if (deviceFound) {
+                fprintf(stderr, "SimCFG: one <device> per customer\n");
+                return false;
+            }
+
+            bool result = ProcessDevice(pChild);
+            if (!result)
+                return false;
+            deviceFound = true;
+        }
+    }
+
+    if (!deviceFound) {
+        fprintf(stderr, "SimCFG: no <device> section found\n");
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Process a <device name="zzz"> chunk.
+ */
+bool PhoneData::ProcessDevice(TiXmlNode* pNode)
+{
+    TiXmlNode* pChild;
+    const char* name;
+
+    assert(pNode->Type() == TiXmlNode::ELEMENT);
+
+    name = pNode->ToElement()->Attribute("name");
+    if (name == NULL) {
+        fprintf(stderr, "SimCFG: <device> requires name attrib\n");
+        return false;
+    }
+    SetName(name);
+
+    /*
+     * Walk through the children and find interesting stuff.
+     *
+     * Might be more correct to process all <display> entries and
+     * then process all <view> entries, since <view> has "pointers"
+     * to <display>.  We're deferring the lookup until later, though,
+     * so for now it doesn't really matter.
+     */
+    for (pChild = pNode->FirstChild(); pChild != NULL;
+        pChild = pChild->NextSibling())
+    {
+        bool result;
+
+        if (pChild->Type() == TiXmlNode::COMMENT)
+            continue;
+
+        if (pChild->Type() == TiXmlNode::ELEMENT &&
+            strcasecmp(pChild->Value(), "title") == 0)
+        {
+            result = ProcessTitle(pChild);
+            if (!result)
+                return false;
+        } else if (pChild->Type() == TiXmlNode::ELEMENT &&
+            strcasecmp(pChild->Value(), "display") == 0)
+        {
+            PhoneDisplay tmpDisplay;
+
+            result = tmpDisplay.ProcessAndValidate(pChild);
+            if (!result)
+                return false;
+
+            mDisplayList.push_back(tmpDisplay);
+        } else if (pChild->Type() == TiXmlNode::ELEMENT &&
+            strcasecmp(pChild->Value(), "keyboard") == 0)
+        {
+            PhoneKeyboard tmpKeyboard;
+            result = tmpKeyboard.ProcessAndValidate(pChild);
+            if (!result)
+                return false;
+                
+            mKeyboardList.push_back(tmpKeyboard);           
+        } else if (pChild->Type() == TiXmlNode::ELEMENT &&
+            strcasecmp(pChild->Value(), "mode") == 0)
+        {
+            PhoneMode tmpMode;
+
+            result = tmpMode.ProcessAndValidate(pChild, mDirectory);
+            if (!result)
+                return false;
+
+            mModeList.push_back(tmpMode);
+        } else {
+            fprintf(stderr, "SimCFG: Warning: unexpected stuff in <device>\n");
+        }
+    }
+
+    if (mDisplayList.size() == 0) {
+        fprintf(stderr, "SimCFG: no <display> entries found\n");
+        return false;
+    }
+    if (mModeList.size() == 0) {
+        fprintf(stderr, "SimCFG: no <mode> entries found\n");
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Handle <title>.
+ */
+bool PhoneData::ProcessTitle(TiXmlNode* pNode)
+{
+    TiXmlNode* pChild;
+
+    pChild = pNode->FirstChild();
+    if (pChild->Type() != TiXmlNode::TEXT) {
+        fprintf(stderr, "SimCFG: title is funky\n");
+        return false;
+    }
+
+    SetTitle(pChild->Value());
+    return true;
+}
+
+
+/*
+ * Load all resources associated with the phone.
+ */
+bool PhoneData::LoadResources(void)
+{
+    typedef List<PhoneMode>::iterator Iter;
+
+    for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii)
+        (*ii).LoadResources();
+    return true;
+}
+
+/*
+ * Unload all resources associated with the phone.
+ */
+bool PhoneData::UnloadResources(void)
+{
+    typedef List<PhoneMode>::iterator Iter;
+
+    for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii)
+        (*ii).UnloadResources();
+    return true;
+}
+
+
+/*
+ * Return the PhoneMode entry with the matching name.
+ *
+ * Returns NULL if no match was found.
+ */
+PhoneMode* PhoneData::GetPhoneMode(const char* modeName)
+{
+    typedef List<PhoneMode>::iterator Iter;
+
+    for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii) {
+        if (strcmp((*ii).GetName(), modeName) == 0)
+            return &(*ii);
+    }
+    return NULL;
+}
+
+/*
+ * Return the Nth phone mode entry.
+ */
+PhoneMode* PhoneData::GetPhoneMode(int idx)
+{
+    typedef List<PhoneMode>::iterator Iter;
+
+    for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii) {
+        if (!idx)
+            return &(*ii);
+        --idx;
+    }
+    return NULL;
+}
+
+
+/*
+ * Return the PhoneDisplay entry with the matching name.
+ *
+ * Returns NULL if no match was found.
+ */
+PhoneDisplay* PhoneData::GetPhoneDisplay(const char* dispName)
+{
+    typedef List<PhoneDisplay>::iterator Iter;
+
+    for (Iter ii = mDisplayList.begin(); ii != mDisplayList.end(); ++ii) {
+        if (strcmp((*ii).GetName(), dispName) == 0)
+            return &(*ii);
+    }
+    return NULL;
+}
+
+/*
+ * Return the Nth phone mode entry.
+ */
+PhoneDisplay* PhoneData::GetPhoneDisplay(int idx)
+{
+    typedef List<PhoneDisplay>::iterator Iter;
+
+    for (Iter ii = mDisplayList.begin(); ii != mDisplayList.end(); ++ii) {
+        if (!idx)
+            return &(*ii);
+        --idx;
+    }
+    return NULL;
+}
+
+/*
+ * Find the PhoneDisplay entry with the matching name, and return its index.
+ *
+ * Returns -1 if the entry wasn't found.
+ */
+int PhoneData::GetPhoneDisplayIndex(const char* dispName)
+{
+    typedef List<PhoneDisplay>::iterator Iter;
+    int idx = 0;
+
+    for (Iter ii = mDisplayList.begin(); ii != mDisplayList.end(); ++ii) {
+        if (strcmp((*ii).GetName(), dispName) == 0)
+            return idx;
+        idx++;
+    }
+    return -1;
+}
+
+
+/*
+ * Return the Nth phone keyboard entry.
+ */
+PhoneKeyboard* PhoneData::GetPhoneKeyboard(int idx)
+{
+    typedef List<PhoneKeyboard>::iterator Iter;
+
+    for (Iter ii = mKeyboardList.begin(); ii != mKeyboardList.end(); ++ii) {
+        if (!idx)
+            return &(*ii);
+        --idx;
+    }
+    return NULL;
+}
diff --git a/simulator/app/PhoneData.h b/simulator/app/PhoneData.h
new file mode 100644
index 0000000..c7f4732
--- /dev/null
+++ b/simulator/app/PhoneData.h
@@ -0,0 +1,365 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Simulated device definition.
+//
+// The "root" of the data structures here is PhoneCollection, which may
+// discard the entire set if the user asks to re-scan the phone definitions.
+// These structures should be considered read-only.
+//
+// PhoneCollection (single global instance)
+//    -->PhoneData
+//       -->PhoneDisplay
+//       -->PhoneMode
+//          -->PhoneView
+//
+#ifndef _SIM_PHONE_DATA_H
+#define _SIM_PHONE_DATA_H
+
+#include <stdio.h>
+#include "tinyxml.h"
+
+#include "PhoneButton.h"
+#include "LoadableImage.h"
+#include <ui/PixelFormat.h>
+#include <utils.h>
+
+
+/*
+ * This represents the keyboard type of the simulated device
+ */
+class PhoneKeyboard {
+public:
+    PhoneKeyboard(void)
+        : mQwerty(false), mKeyMap(NULL)
+        {}       
+    ~PhoneKeyboard(void) {
+        free((void*)mKeyMap);
+    }    
+ 
+    PhoneKeyboard(const PhoneKeyboard& src)
+        : mQwerty(false), mKeyMap(NULL)
+    {
+        CopyMembers(src);
+    }
+    PhoneKeyboard& operator=(const PhoneKeyboard& src) {
+        if (this != &src)       // self-assignment
+            CopyMembers(src);
+        return *this;
+    }
+    void CopyMembers(const PhoneKeyboard& src) {
+        mQwerty = src.mQwerty;
+        mKeyMap = src.mKeyMap ? strdup(src.mKeyMap) : NULL;
+    }
+    
+    bool ProcessAndValidate(TiXmlNode* pNode);
+    
+    bool getQwerty() { return mQwerty; }    
+
+    const char *getKeyMap() { return mKeyMap; }
+private:
+    bool    mQwerty;
+    const char * mKeyMap;
+};
+
+/*
+ * This represents a single display device, usually an LCD screen.
+ * It also includes an optional surrounding graphic, usually a picture of
+ * the device itself.
+ */
+class PhoneDisplay {
+public:
+    PhoneDisplay(void)
+        : mName(NULL)
+        {}
+    ~PhoneDisplay(void) {
+        delete[] mName;
+    }
+
+    PhoneDisplay(const PhoneDisplay& src)
+        : mName(NULL)
+    {
+        CopyMembers(src);
+    }
+    PhoneDisplay& operator=(const PhoneDisplay& src) {
+        if (this != &src)       // self-assignment
+            CopyMembers(src);
+        return *this;
+    }
+    void CopyMembers(const PhoneDisplay& src) {
+        // Can't memcpy and member-copy the container classes, because the
+        // containers have already been constructed, and for operator= they
+        // might even have stuff in them.
+        delete[] mName;
+        mName = android::strdupNew(src.mName);
+        mWidth = src.mWidth;
+        mHeight = src.mHeight;
+        mFormat = src.mFormat;
+        mRefresh = src.mRefresh;
+    }
+
+    bool ProcessAndValidate(TiXmlNode* pNode);
+
+    const char* GetName(void) const { return mName; }
+    int GetWidth(void) const { return mWidth; }
+    int GetHeight(void) const { return mHeight; }
+    android::PixelFormat GetFormat(void) const { return mFormat; }
+    int GetRefresh(void) const { return mRefresh; }
+
+    static bool IsCompatible(PhoneDisplay* pDisplay1, PhoneDisplay* pDisplay2);
+
+private:
+    char*           mName;
+
+    // display dimensions, in pixels
+    int             mWidth;
+    int             mHeight;
+
+    // frame buffer format
+    android::PixelFormat mFormat;
+
+    // display refresh rate, in fps
+    int             mRefresh;
+};
+
+/*
+ * This is a "view" of a device, which includes the display, a background
+ * image, and perhaps some clickable keys for input.
+ *
+ * Because the key graphics are associated with a particular display, we
+ * hold a list of keys here.  (It also allows the possibility of handling
+ * a situation where the same key shows up in multiple background images,
+ * e.g. a flip phone with a "volume" key on the side.  If we include the
+ * key in both places, we can highlight it on both displays.)
+ */
+class PhoneView {
+public:
+    PhoneView(void)
+        : mDisplayName(NULL)
+        {}
+    ~PhoneView(void) {
+        delete[] mDisplayName;
+    }
+
+    PhoneView(const PhoneView& src) {
+        CopyMembers(src);
+    }
+    PhoneView& operator=(const PhoneView& src) {
+        if (this != &src)       // self-assignment
+            CopyMembers(src);
+        return *this;
+    }
+    void CopyMembers(const PhoneView& src) {
+        // Can't memcpy and member-copy the container classes, because the
+        // containers have already been constructed, and for operator= they
+        // might even have stuff in them.
+        mImageList = src.mImageList;
+        mButtonList = src.mButtonList;
+        mDisplayName = android::strdupNew(src.mDisplayName);
+        mXOffset = src.mXOffset;
+        mYOffset = src.mYOffset;
+        mRotation = src.mRotation;
+    }
+
+    // load or unload resources, e.g. wxBitmaps from image files
+    bool LoadResources(void);
+    bool UnloadResources(void);
+
+    // simple accessors
+    int GetXOffset(void) const { return mXOffset; }
+    int GetYOffset(void) const { return mYOffset; }
+    const char* GetDisplayName(void) const { return mDisplayName; }
+
+    // image list access
+    int GetBkgImageCount(void) const;
+    const LoadableImage* GetBkgImage(int idx) const;
+
+    // find the first button that covers the specified coords
+    PhoneButton* FindButtonHit(int x, int y);
+
+    // find the first button with a matching key code
+    PhoneButton* FindButtonByKey(KeyCode keyCode);
+
+    bool ProcessAndValidate(TiXmlNode* pNode, const char* directory);
+    bool ProcessImage(TiXmlNode* pNode, const char* directory);
+    bool ProcessButton(TiXmlNode* pNode, const char* directory);
+
+private:
+    // background images for the phone picture that surrounds the display
+    android::List<LoadableImage> mImageList;
+
+    // list of accessible buttons, some of which have highlight graphics
+    android::List<PhoneButton>  mButtonList;
+
+    char*           mDisplayName;
+
+    // these determine where in the image the display output goes
+    int             mXOffset;
+    int             mYOffset;
+
+    // clockwise rotation of the output; sim must rotate in opposite direction
+    typedef enum Rotation {
+        kRotUnknown = 0,
+        kRot0,
+        kRot90,
+        kRot180,
+        kRot270,
+    } Rotation;
+    Rotation        mRotation;
+};
+
+/*
+ * One mode of a phone.  Simple devices only have one mode.  Flip phones
+ * have two (opened and closed).  Other devices might have more.  The
+ * mode is communicated to the runtime because it may need to process
+ * input events differently.
+ */
+class PhoneMode {
+public:
+    PhoneMode(void)
+        : mName(NULL)
+        {}
+    ~PhoneMode(void) {
+        delete[] mName;
+    }
+
+    PhoneMode(const PhoneMode& src)
+        : mName(NULL)
+    {
+        CopyMembers(src);
+    }
+    PhoneMode& operator=(const PhoneMode& src) {
+        if (this != &src)       // self-assignment
+            CopyMembers(src);
+        return *this;
+    }
+    void CopyMembers(const PhoneMode& src) {
+        delete[] mName;
+        mName = android::strdupNew(src.mName);
+        mViewList = src.mViewList;
+    }
+
+
+    // load or unload resources for this object and all members of mViewList
+    bool LoadResources(void);
+    bool UnloadResources(void);
+
+    // get the #of views
+    int GetNumViews(void) const { return mViewList.size(); }
+    // get the Nth display
+    PhoneView* GetPhoneView(int viewNum);
+
+    const char* GetName(void) const { return mName; }
+    void SetName(const char* name) {
+        delete[] mName;
+        mName = android::strdupNew(name);
+    }
+
+    // load the <mode> section from the config file
+    bool ProcessAndValidate(TiXmlNode* pNode, const char* directory);
+
+private:
+    char*           mName;
+
+    android::List<PhoneView> mViewList;
+};
+
+/*
+ * This holds the data for one device.
+ *
+ * Each device may have multiple "modes", e.g. a flip-phone that can be
+ * open or shut.  Each mode has different configurations for the visible
+ * displays and active keys.
+ */
+class PhoneData {
+public:
+    PhoneData(void) :
+        mName(NULL), mTitle(NULL), mDirectory(NULL)
+        {}
+    virtual ~PhoneData(void) {
+        delete[] mName;
+        delete[] mTitle;
+        delete[] mDirectory;
+    }
+
+    PhoneData(const PhoneData& src)
+        : mName(NULL), mTitle(NULL), mDirectory(NULL)
+    {
+        CopyMembers(src);
+    }
+    PhoneData& operator=(const PhoneData& src) {
+        if (this != &src)       // self-assignment
+            CopyMembers(src);
+        return *this;
+    }
+    void CopyMembers(const PhoneData& src) {
+        delete[] mName;
+        delete[] mTitle;
+        delete[] mDirectory;
+        mName = android::strdupNew(src.mName);
+        mTitle = android::strdupNew(src.mTitle);
+        mDirectory = android::strdupNew(src.mDirectory);
+        mModeList = src.mModeList;
+        mDisplayList = src.mDisplayList;
+        mKeyboardList = src.mKeyboardList;
+    }
+
+    // initialize the object with the phone data in the specified dir
+    bool Create(const char* directory);
+
+    // load or unload resources, e.g. wxBitmaps from image files
+    bool LoadResources(void);
+    bool UnloadResources(void);
+
+    // simple accessors
+    const char* GetName(void) const { return mName; }
+    void SetName(const char* name) {
+        delete[] mName;
+        mName = android::strdupNew(name);
+    }
+    const char* GetTitle(void) const { return mTitle; }
+    void SetTitle(const char* title) {
+        delete[] mTitle;
+        mTitle = android::strdupNew(title);
+    }
+    const char* GetDirectory(void) const { return mDirectory; }
+    void SetDirectory(const char* dir) {
+        delete[] mDirectory;
+        mDirectory = android::strdupNew(dir);
+    }
+
+    
+    // get number of modes
+    int GetNumModes(void) const { return mModeList.size(); }
+    // get the specified mode object
+    PhoneMode* GetPhoneMode(int idx);
+    PhoneMode* GetPhoneMode(const char* modeName);
+
+    // get number of displays
+    int GetNumDisplays(void) const { return mDisplayList.size(); }
+    // get the specified display object
+    PhoneDisplay* GetPhoneDisplay(int idx);
+    PhoneDisplay* GetPhoneDisplay(const char* displayName);
+    // get the index of the matching display
+    int GetPhoneDisplayIndex(const char* displayName);
+
+    // get number of keyboards
+    int GetNumKeyboards(void) const { return mKeyboardList.size(); }
+    // get the specified display object
+    PhoneKeyboard* GetPhoneKeyboard(int idx);
+    
+private:
+    bool ProcessAndValidate(TiXmlDocument* pDoc);
+    bool ProcessDevice(TiXmlNode* pNode);
+    bool ProcessTitle(TiXmlNode* pNode);
+
+    char*           mName;
+    char*           mTitle;
+    char*           mDirectory;
+
+    android::List<PhoneMode> mModeList;
+    android::List<PhoneDisplay> mDisplayList;
+    android::List<PhoneKeyboard> mKeyboardList;
+};
+
+#endif // _SIM_PHONE_DATA_H
diff --git a/simulator/app/PhoneWindow.cpp b/simulator/app/PhoneWindow.cpp
new file mode 100644
index 0000000..60a9809
--- /dev/null
+++ b/simulator/app/PhoneWindow.cpp
@@ -0,0 +1,1051 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Displays the phone image and handles user input.
+//
+
+// 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"   // needed for Windows build
+#include "wx/dcbuffer.h"
+
+#include "LinuxKeys.h"
+#include "PhoneWindow.h"
+#include "DeviceWindow.h"
+#include "PhoneData.h"
+#include "PhoneCollection.h"
+#include "MainFrame.h"
+#include "MyApp.h"
+
+using namespace android;
+
+BEGIN_EVENT_TABLE(PhoneWindow, wxWindow)    // NOT wxDialog
+    EVT_ACTIVATE(PhoneWindow::OnActivate)
+    //EVT_ACTIVATE_APP(PhoneWindow::OnActivate)
+    EVT_CLOSE(PhoneWindow::OnClose)
+    EVT_MOVE(PhoneWindow::OnMove)
+    EVT_ERASE_BACKGROUND(PhoneWindow::OnErase)
+    EVT_PAINT(PhoneWindow::OnPaint)
+
+    EVT_KEY_DOWN(PhoneWindow::OnKeyDown)
+    EVT_KEY_UP(PhoneWindow::OnKeyUp)
+    EVT_LEFT_DOWN(PhoneWindow::OnMouseLeftDown)
+    EVT_LEFT_DCLICK(PhoneWindow::OnMouseLeftDown)
+    EVT_LEFT_UP(PhoneWindow::OnMouseLeftUp)
+    EVT_RIGHT_DOWN(PhoneWindow::OnMouseRightDown)
+    EVT_RIGHT_DCLICK(PhoneWindow::OnMouseRightDown)
+    EVT_RIGHT_UP(PhoneWindow::OnMouseRightUp)
+    EVT_MOTION(PhoneWindow::OnMouseMotion)
+    EVT_LEAVE_WINDOW(PhoneWindow::OnMouseLeaveWindow)
+    EVT_TIMER(kVibrateTimerId, PhoneWindow::OnTimer)
+END_EVENT_TABLE()
+
+
+/*
+ * Create a new PhoneWindow.  This should be a child of the main frame.
+ */
+PhoneWindow::PhoneWindow(wxWindow* parent, const wxPoint& posn)
+    : wxDialog(parent, wxID_ANY, wxT("Device"), posn, wxDefaultSize,
+        wxDEFAULT_DIALOG_STYLE),
+      mpMOHViewIndex(-1),
+      mpMOHButton(NULL),
+      mMouseKeySent(kKeyCodeUnknown),
+      mpViewInfo(NULL),
+      mNumViewInfo(0),
+      mpDeviceWindow(NULL),
+      mNumDeviceWindows(0),
+      mPhoneModel(-1),
+      mCurrentMode(wxT("(unknown)")),
+      mPlacementChecked(false),
+      mpParent((MainFrame*)parent),
+      mTimer(this, kVibrateTimerId),
+      mTrackingTouch(false)
+{
+    SetBackgroundColour(*wxLIGHT_GREY);
+    SetBackgroundStyle(wxBG_STYLE_CUSTOM);
+
+    //SetCursor(wxCursor(wxCURSOR_HAND));     // a bit distracting (pg.276)
+}
+
+/*
+ * Destroy everything we own.
+ *
+ * This might be called well after we've been closed and another
+ * PhoneWindow has been created, because wxWidgets likes to defer things.
+ */
+PhoneWindow::~PhoneWindow(void)
+{
+    //printf("--- ~PhoneWindow %p\n", this);
+    delete[] mpViewInfo;
+    if (mpDeviceWindow != NULL) {
+        for (int i = 0; i < mNumDeviceWindows; i++) {
+            /* make sure they don't try to use our member */
+            mpDeviceWindow[i]->DeviceManagerClosing();
+            /* make sure the child window gets destroyed -- not necessary? */
+            mpDeviceWindow[i]->Destroy();
+        }
+
+        /* delete our array of pointers */
+        delete[] mpDeviceWindow;
+    }
+}
+
+/*
+ * Check for an updated runtime when window becomes active
+ */
+void PhoneWindow::OnActivate(wxActivateEvent& event)
+{
+    /*
+     * DO NOT do this.  Under Windows, it causes the parent window to get
+     * an activate event, which causes our parent to get the focus.  With
+     * this bit of code active it is impossible for the phone window to
+     * receive user input.
+     */
+    //GetParent()->AddPendingEvent(event);
+
+    // If we are being deactivated, go ahead and send key up events so that the
+    // runtime doesn't think we are holding down the key. Issue #685750
+    if (!event.GetActive()) {
+        ListIter iter;
+        for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ) {
+            KeyCode keyCode = (*iter).GetKeyCode();
+            GetDeviceManager()->SendKeyEvent(keyCode, false);
+            iter = mPressedKeys.erase(iter);
+        }
+    }
+}
+
+/*
+ * Close the phone window.
+ */
+void PhoneWindow::OnClose(wxCloseEvent& event)
+{
+    //printf("--- PhoneWindow::OnClose %p\n", this);
+#if 0
+    if (mDeviceManager.IsRunning() && !mDeviceManager.IsKillable()) {
+        printf("Sim: refusing to close window on external runtime\n");
+        event.Veto();
+        return;
+    }
+#endif
+
+    wxRect rect = GetRect();
+    printf("Sim: Closing phone window (posn=(%d,%d))\n", rect.x, rect.y);
+
+    /* notify others */
+    mpParent->PhoneWindowClosing(rect.x, rect.y);
+    mDeviceManager.WindowsClosing();
+
+    /* end it all */
+    Destroy();
+}
+
+/*
+ * Prep the PhoneWindow to display a specific phone model.  Pass in the
+ * model index.
+ *
+ * This gets called whenever the display changes.  This could be a new
+ * device with identical characteristics, or a different mode for the same
+ * device.
+ *
+ * The window can be re-used so long as the display characteristics are
+ * the same.  If the display characteristics are different, we have to
+ * restart the device.
+ */
+bool PhoneWindow::Setup(int phoneIdx)
+{
+    wxString fileName;
+    PhoneCollection* pCollection = PhoneCollection::GetInstance();
+
+    if (phoneIdx < 0 || phoneIdx >= pCollection->GetPhoneCount()) {
+        fprintf(stderr, "Bogus phone index %d\n", phoneIdx);
+        return false;
+    }
+
+    /*
+     * Clear these out so that failure here is noticeable to caller.  We
+     * regenerate the ViewInfo array every time, because the set of Views
+     * is different for every Mode.
+     */
+    delete[] mpViewInfo;
+    mpViewInfo = NULL;
+    mNumViewInfo = -1;
+
+    PhoneData* pPhoneData;
+    PhoneMode* pPhoneMode;
+    PhoneView* pPhoneView;
+
+    pPhoneData = pCollection->GetPhoneData(phoneIdx);
+
+    pPhoneMode = pPhoneData->GetPhoneMode(GetCurrentMode().ToAscii());
+    if (pPhoneMode == NULL) {
+        fprintf(stderr, "current mode (%s) not known\n",
+            (const char*) GetCurrentMode().ToAscii());
+        return false;
+    }
+
+    int numViews = pPhoneMode->GetNumViews();
+    if (numViews == 0) {
+        fprintf(stderr, "Phone %d mode %s has no views\n",
+            phoneIdx, pPhoneMode->GetName());
+        return false;
+    }
+
+    const int kBorder = 2;
+    int i;
+    int maxHeight = 0;
+    int fullWidth = kBorder;
+    ViewInfo* pViewInfo;
+
+    pViewInfo = new ViewInfo[numViews];
+
+    /* figure out individual and overall dimensions */
+    for (i = 0; i < numViews; i++) {
+        pPhoneView = pPhoneMode->GetPhoneView(i);
+        if (pPhoneView == NULL) {
+            fprintf(stderr, "view %d not found\n", i);
+            return false;
+        }
+
+        if (!GetDimensions(pPhoneData, pPhoneView, &pViewInfo[i]))
+            return false;
+
+        if (maxHeight < pViewInfo[i].GetHeight())
+            maxHeight = pViewInfo[i].GetHeight();
+        fullWidth += pViewInfo[i].GetWidth() + kBorder;
+    }
+
+    /* create the device windows if we don't already have them */
+    if (mpDeviceWindow == NULL) {
+        mNumDeviceWindows = pPhoneData->GetNumDisplays();
+        mpDeviceWindow = new DeviceWindow*[mNumDeviceWindows];
+        if (mpDeviceWindow == NULL)
+            return false;
+
+        for (i = 0; i < mNumDeviceWindows; i++) {
+            mpDeviceWindow[i] = new DeviceWindow(this, &mDeviceManager);
+        }
+    } else {
+        assert(pPhoneData->GetNumDisplays() == mNumDeviceWindows);
+    }
+
+    /*
+     * Position device windows within their views, taking into account
+     * border areas.
+     */
+    int shift = kBorder;
+    for (i = 0; i < numViews; i++) {
+        int displayIdx;
+        PhoneDisplay* pPhoneDisplay;
+
+        displayIdx = pViewInfo[i].GetDisplayIndex();
+        pPhoneDisplay = pPhoneData->GetPhoneDisplay(displayIdx);
+        //printf("View %d: display %d\n", i, displayIdx);
+
+        pViewInfo[i].SetX(shift);
+        pViewInfo[i].SetY(kBorder);
+
+        mpDeviceWindow[displayIdx]->SetSize(
+            pViewInfo[i].GetX() + pViewInfo[i].GetDisplayX(),
+            pViewInfo[i].GetY() + pViewInfo[i].GetDisplayY(),
+            pPhoneDisplay->GetWidth(), pPhoneDisplay->GetHeight());
+
+        // incr by width of view
+        shift += pViewInfo[i].GetWidth() + kBorder;
+    }
+
+    /* configure the device manager if it's not already running */
+    if (!mDeviceManager.IsInitialized()) {
+        mDeviceManager.Init(pPhoneData->GetNumDisplays(), mpParent);
+
+        for (i = 0; i < pPhoneData->GetNumDisplays(); i++) {
+            PhoneDisplay* pPhoneDisplay;
+            bool res;
+
+            pPhoneDisplay = pPhoneData->GetPhoneDisplay(i);
+
+            res = mDeviceManager.SetDisplayConfig(i, mpDeviceWindow[i],
+                pPhoneDisplay->GetWidth(), pPhoneDisplay->GetHeight(),
+                pPhoneDisplay->GetFormat(), pPhoneDisplay->GetRefresh());
+            if (!res) {
+                fprintf(stderr, "Sim: ERROR: could not configure device mgr\n");
+                return false;
+            }
+        }
+        const char *kmap = pPhoneData->GetPhoneKeyboard(0)->getKeyMap();
+        mDeviceManager.SetKeyboardConfig(kmap);
+    } else {
+        assert(pPhoneData->GetNumDisplays() == mDeviceManager.GetNumDisplays());
+    }
+
+    /*
+     * Success.  Finish up.
+     */
+    mPhoneModel = phoneIdx;
+    mpViewInfo = pViewInfo;
+    mNumViewInfo = numViews;
+
+    /* set up our window */
+    SetClientSize(fullWidth, maxHeight + kBorder * 2);
+    SetBackgroundColour(*wxLIGHT_GREY);
+    //SetBackgroundColour(*wxBLUE);
+    SetTitle(wxString::FromAscii(pPhoneData->GetTitle()));
+
+    SetFocus();     // set keyboard input focus
+
+    return true;
+}
+
+/*
+ * The device table has been reloaded.  We need to throw out any pointers
+ * we had into it and possibly reload some stuff.
+ */
+void PhoneWindow::DevicesRescanned(void)
+{
+    mpMOHButton = NULL;
+    mpMOHViewIndex = -1;
+
+    /*
+     * Re-evaluate phone definition.  There is an implicit assumption
+     * that the re-scanned version is compatible with the previous
+     * version (i.e. it still exists and has the same screen size).
+     *
+     * We're also currently assuming that no phone definitions have been
+     * added or removed, which is bad -- we should get the new index for
+     * for phone by searching for it by name.
+     *
+     * TODO: don't make these assumptions.
+     */
+    Setup(mPhoneModel);
+}
+
+/*
+ * Check the initial placement of the window.  We get one of these messages
+ * when the window is first placed, and every time it's moved thereafter.
+ *
+ * Right now we're just trying to make sure wxWidgets doesn't shove it off
+ * the top of the screen under Linux.  Might want to change this to
+ * remember the previous placement and put the window back.
+ */
+void PhoneWindow::OnMove(wxMoveEvent& event)
+{
+    if (mPlacementChecked)
+        return;
+
+    wxPoint point;
+    point = event.GetPosition();
+    if (point.y < 0) {
+        printf("Sim: window is at (%d,%d), adjusting\n", point.x, point.y);
+        point.y = 0;
+        Move(point);
+    }
+
+    mPlacementChecked = true;
+}
+
+/*
+ * Figure out the dimensions required to contain the specified view.
+ *
+ * This is usually the size of the background image, but if we can't
+ * load it or it's too small just create a trivial window.
+ */
+bool PhoneWindow::GetDimensions(PhoneData* pPhoneData, PhoneView* pPhoneView,
+    ViewInfo* pInfo)
+{
+    PhoneDisplay* pPhoneDisplay;
+    int xoff=0, yoff=0, width, height;
+    int displayIdx;
+
+    displayIdx = pPhoneData->GetPhoneDisplayIndex(pPhoneView->GetDisplayName());
+    if (displayIdx < 0)
+        return false;
+
+    pPhoneDisplay = pPhoneData->GetPhoneDisplay(displayIdx);
+    if (pPhoneDisplay == NULL) {
+        fprintf(stderr, "display '%s' not found in device '%s'\n",
+            pPhoneView->GetDisplayName(), pPhoneData->GetName());
+        return false;
+    }
+
+    // load images for this phone
+    (void) pPhoneView->LoadResources();
+
+    width = height = 0;
+
+    // by convention, the background bitmap is the first image in the list
+    if (pPhoneView->GetBkgImageCount() > 0) {
+        wxBitmap* pBitmap = pPhoneView->GetBkgImage(0)->GetBitmap();
+        if (pBitmap != NULL) {
+            // size window to match bitmap
+            xoff = pPhoneView->GetXOffset();
+            yoff = pPhoneView->GetYOffset();
+            width = pBitmap->GetWidth();
+            height = pBitmap->GetHeight();
+        }
+    }
+
+    // no bitmap, or bitmap is smaller than display
+    if (width < pPhoneDisplay->GetWidth() ||
+        height < pPhoneDisplay->GetHeight())
+    {
+        // create window to just hold display
+        xoff = yoff = 0;
+        width = pPhoneDisplay->GetWidth();
+        height = pPhoneDisplay->GetHeight();
+    }
+    if (width <= 0 || height <= 0) {
+        fprintf(stderr, "ERROR: couldn't determine display size\n");
+        return false;
+    }
+
+    pInfo->SetX(0);
+    pInfo->SetY(0);             // another function determines these
+    pInfo->SetDisplayX(xoff);
+    pInfo->SetDisplayY(yoff);
+    pInfo->SetWidth(width);
+    pInfo->SetHeight(height);
+    pInfo->SetDisplayIndex(displayIdx);
+
+    //printf("xoff=%d yoff=%d width=%d height=%d index=%d\n",
+    //    pInfo->GetDisplayX(), pInfo->GetDisplayY(),
+    //    pInfo->GetWidth(), pInfo->GetHeight(), pInfo->GetDisplayIndex());
+
+    return true;
+}
+
+/*
+ * Return PhoneData pointer for the current phone model.
+ */
+PhoneData* PhoneWindow::GetPhoneData(void) const
+{
+    PhoneCollection* pCollection = PhoneCollection::GetInstance();
+    return pCollection->GetPhoneData(mPhoneModel);
+}
+
+/*
+ * Convert a wxWidgets key code into a device key code.
+ *
+ * Someday we may want to make this configurable.
+ *
+ * NOTE: we need to create a mapping between simulator key and desired
+ * function.  The "return" key should always mean "select", whether
+ * it's a "select" button or pressing in on the d-pad.  Ditto for
+ * the arrow keys, whether we have a joystick, d-pad, or four buttons.
+ * Each key here should have a set of things that it could possibly be,
+ * and we match it up with the set of buttons actually defined for the
+ * phone.  [for convenience, need to ensure that buttons need not have
+ * an associated image]
+ */
+int PhoneWindow::ConvertKeyCode(int wxKeyCode) const
+{
+    switch (wxKeyCode) {
+    case WXK_NUMPAD_INSERT:
+    case WXK_NUMPAD0:
+    case '0':                   return KEY_0;
+    case WXK_NUMPAD_HOME:
+    case WXK_NUMPAD1:
+    case '1':                   return KEY_1;
+    case WXK_NUMPAD_UP:
+    case WXK_NUMPAD2:
+    case '2':                   return KEY_2;
+    case WXK_NUMPAD_PRIOR:
+    case WXK_NUMPAD3:
+    case '3':                   return KEY_3;
+    case WXK_NUMPAD_LEFT:
+    case WXK_NUMPAD4:
+    case '4':                   return KEY_4;
+    case WXK_NUMPAD_BEGIN:
+    case WXK_NUMPAD5:
+    case '5':                   return KEY_5;
+    case WXK_NUMPAD_RIGHT:
+    case WXK_NUMPAD6:
+    case '6':                   return KEY_6;
+    case WXK_NUMPAD_END:
+    case WXK_NUMPAD7:
+    case '7':                   return KEY_7;
+    case WXK_NUMPAD_DOWN:
+    case WXK_NUMPAD8:
+    case '8':                   return KEY_8;
+    case WXK_NUMPAD_NEXT:
+    case WXK_NUMPAD9:
+    case '9':                   return KEY_9;
+    case WXK_NUMPAD_MULTIPLY:   return KEY_SWITCHVIDEOMODE; //kKeyCodeStar;
+    case WXK_LEFT:              return KEY_LEFT;
+    case WXK_RIGHT:             return KEY_RIGHT;
+    case WXK_UP:                return KEY_UP;
+    case WXK_DOWN:              return KEY_DOWN;
+    case WXK_NUMPAD_ENTER:      return KEY_REPLY; //kKeyCodeDpadCenter;
+    case WXK_HOME:              return KEY_HOME;
+    case WXK_PRIOR:
+    case WXK_PAGEUP:            return KEY_MENU; //kKeyCodeSoftLeft;
+    case WXK_NEXT:
+    case WXK_PAGEDOWN:          return KEY_KBDILLUMUP; //kKeyCodeSoftRight;
+    case WXK_DELETE:            
+    case WXK_BACK:              return KEY_BACKSPACE; //kKeyCodeDel;
+    case WXK_ESCAPE:
+    case WXK_END:               return KEY_BACK; //kKeyCodeBack;
+    case WXK_NUMPAD_DELETE:
+    case WXK_NUMPAD_DECIMAL:    return KEY_KBDILLUMTOGGLE; //kKeyCodePound;
+    case WXK_SPACE:             return KEY_SPACE; //kKeyCodeSpace;
+    case WXK_RETURN:            return KEY_ENTER; //kKeyCodeNewline;
+    case WXK_F3:                return KEY_F3; //kKeyCodeCall;
+    case WXK_F4:                return KEY_F4; //kKeyCodeEndCall;
+    case WXK_NUMPAD_ADD:
+    case WXK_F5:                return KEY_VOLUMEUP;
+    case WXK_NUMPAD_SUBTRACT:
+    case WXK_F6:                return KEY_VOLUMEDOWN;
+    case WXK_F7:                return KEY_POWER;
+    case WXK_F8:                return KEY_CAMERA;
+    case 'A':                   return KEY_A;
+    case 'B':                   return KEY_B;
+    case 'C':                   return KEY_C;
+    case 'D':                   return KEY_D;
+    case 'E':                   return KEY_E;
+    case 'F':                   return KEY_F;
+    case 'G':                   return KEY_G;
+    case 'H':                   return KEY_H;
+    case 'I':                   return KEY_I;
+    case 'J':                   return KEY_J;
+    case 'K':                   return KEY_K;
+    case 'L':                   return KEY_L;
+    case 'M':                   return KEY_M;
+    case 'N':                   return KEY_N;
+    case 'O':                   return KEY_O;
+    case 'P':                   return KEY_P;
+    case 'Q':                   return KEY_Q;
+    case 'R':                   return KEY_R;
+    case 'S':                   return KEY_S;
+    case 'T':                   return KEY_T;
+    case 'U':                   return KEY_U;
+    case 'V':                   return KEY_V;
+    case 'W':                   return KEY_W;
+    case 'X':                   return KEY_X;
+    case 'Y':                   return KEY_Y;
+    case 'Z':                   return KEY_Z;
+    case ',':                   return KEY_COMMA;
+    case '.':                   return KEY_DOT;
+    case '<':                   return KEY_COMMA;
+    case '>':                   return KEY_DOT;
+    case '`':                   return KEY_GREEN; /*KEY_GRAVE;*/
+    case '-':                   return KEY_MINUS;
+    case '=':                   return KEY_EQUAL;
+    case '[':                   return KEY_LEFTBRACE;
+    case ']':                   return KEY_RIGHTBRACE;
+    case '\\':                  return KEY_BACKSLASH;
+    case ';':                   return KEY_SEMICOLON;
+    case '\'':                  return KEY_APOSTROPHE;
+    case '/':                   return KEY_SLASH;
+    case WXK_SHIFT:             return KEY_LEFTSHIFT;
+    case WXK_CONTROL:
+    case WXK_ALT:               return KEY_LEFTALT;
+    case WXK_TAB:               return KEY_TAB;
+    // don't show "ignoring key" message for these
+    case WXK_MENU:
+        break;
+    default:
+        printf("(ignoring key %d)\n", wxKeyCode);
+        break;
+    }
+
+    return kKeyCodeUnknown;
+}
+
+
+/*
+ * Keyboard handling.  These get converted into Android-defined key
+ * constants here.
+ *
+ * NOTE: would be nice to handle menu keyboard accelerators here.
+ * Simply stuffing the key events into MainFrame with AddPendingEvent
+ * didn't seem to do the trick.
+ */
+void PhoneWindow::OnKeyDown(wxKeyEvent& event)
+{
+    KeyCode keyCode;
+
+    keyCode = (KeyCode) ConvertKeyCode(event.GetKeyCode());
+    if (keyCode != kKeyCodeUnknown) {
+        if (!IsKeyPressed(keyCode)) {
+            //printf("PW: down: key %d\n", keyCode);
+            GetDeviceManager()->SendKeyEvent(keyCode, true);
+            AddPressedKey(keyCode);
+        }
+    } else {
+        //printf("PW: down: %d\n", event.GetKeyCode());
+        event.Skip();       // not handled by us
+    }
+}
+
+/*
+ * Pass key-up events to runtime.
+ */
+void PhoneWindow::OnKeyUp(wxKeyEvent& event)
+{
+    KeyCode keyCode;
+
+    keyCode = (KeyCode) ConvertKeyCode(event.GetKeyCode());
+    if (keyCode != kKeyCodeUnknown) {
+        // Send the key event if we already have this key pressed.
+        if (IsKeyPressed(keyCode)) {
+            //printf("PW:   up: key %d\n", keyCode);
+            GetDeviceManager()->SendKeyEvent(keyCode, false);
+            RemovePressedKey(keyCode);
+        }
+    } else {
+        //printf("PW:   up: %d\n", event.GetKeyCode());
+        event.Skip();       // not handled by us
+    }
+}
+
+/*
+ * Mouse handling.
+ *
+ * Unlike more conventional button tracking, we highlight on mouse-over
+ * and send the key on mouse-down.  This behavior may be confusing for
+ * people expecting standard behavior, but it allows us to simulate the
+ * effect of holding a key down.
+ *
+ * We want to catch both "down" and "double click" events; otherwise
+ * fast clicking results in a lot of discarded events.
+ */
+void PhoneWindow::OnMouseLeftDown(wxMouseEvent& event)
+{
+    if (mpMOHButton != NULL) {
+        //printf("PW: left down\n");
+        KeyCode keyCode = mpMOHButton->GetKeyCode();
+        GetDeviceManager()->SendKeyEvent(keyCode, true);
+        mMouseKeySent = keyCode;
+        AddPressedKey(keyCode);
+    } else {
+        int screenX, screenY;
+
+        if (GetTouchPosition(event, &screenX, &screenY)) {
+            //printf("TOUCH at %d,%d\n", screenX, screenY);
+            mTrackingTouch = true;
+            mTouchX = screenX;
+            mTouchY = screenY;
+            GetDeviceManager()->SendTouchEvent(Simulator::kTouchDown,
+                mTouchX, mTouchY);
+        } else {
+            //printf("(ignoring left click)\n");
+        }
+    }
+}
+
+/*
+ * Left button has been released.  Do something clever.
+ *
+ * On some platforms we will lose this if the mouse leaves the window.
+ */
+void PhoneWindow::OnMouseLeftUp(wxMouseEvent& WXUNUSED(event))
+{
+    if (mMouseKeySent != kKeyCodeUnknown) {
+        //printf("PW: left up\n");
+        GetDeviceManager()->SendKeyEvent(mMouseKeySent, false);
+        RemovePressedKey(mMouseKeySent);
+    } else {
+        if (mTrackingTouch) {
+            //printf("TOUCH release (last was %d,%d)\n", mTouchX, mTouchY);
+            mTrackingTouch = false;
+            GetDeviceManager()->SendTouchEvent(Simulator::kTouchUp,
+                mTouchX, mTouchY);
+        } else {
+            //printf("(ignoring left-up)\n");
+        }
+    }
+    mMouseKeySent = kKeyCodeUnknown;
+}
+
+void PhoneWindow::OnMouseRightDown(wxMouseEvent& event)
+{
+    //printf("(ignoring right-down)\n");
+}
+void PhoneWindow::OnMouseRightUp(wxMouseEvent& event)
+{
+    //printf("(ignoring right-up)\n");
+}
+
+/*
+ * Track mouse motion so we can do mouse-over button highlighting.
+ */
+void PhoneWindow::OnMouseMotion(wxMouseEvent& event)
+{
+    /*
+     * If the mouse motion event occurred inside the device window,
+     * we treat it differently than mouse movement over the picture of
+     * the device.
+     */
+    if (event.GetEventObject() == mpDeviceWindow[0]) {
+        if (mpMOHViewIndex >= 0) {
+            /* can happen if the mouse moves fast enough */
+            //printf("Mouse now in dev window, clearing button highlight\n");
+            mpMOHViewIndex = -1;
+            mpMOHButton = NULL;
+            Refresh();
+        }
+
+        if (!event.LeftIsDown() && event.RightIsDown()) {
+            /* right-button movement */
+            //printf("(ignoring right-drag)\n");
+            return;
+        }
+
+        //printf("moveto: %d,%d\n", event.m_x, event.m_y);
+
+        int screenX, screenY;
+        if (mTrackingTouch) {
+            if (GetTouchPosition(event, &screenX, &screenY)) {
+                //printf("TOUCH moved to %d,%d\n", screenX, screenY);
+                mTouchX = screenX;
+                mTouchY = screenY;
+                GetDeviceManager()->SendTouchEvent(Simulator::kTouchDrag,
+                    mTouchX, mTouchY);
+            } else {
+                //printf("TOUCH moved off screen\n");
+            }
+        }
+
+        return;
+    }
+
+    PhoneData* pPhoneData = GetPhoneData();
+    if (pPhoneData == NULL)
+        return;
+
+    /*
+     * Check to see if we're on top of a button.  If our "on top of
+     * something" state has changed, force a redraw.
+     *
+     * We have to run through the list of Views and check all of the
+     * buttons in each.
+     */
+    PhoneMode* pMode = pPhoneData->GetPhoneMode(GetCurrentMode().ToAscii());
+    if (pMode == NULL)
+        return;
+
+    int viewIndex = -1;
+    PhoneButton* pHighlight = NULL;
+    int i;
+
+    for (i = pMode->GetNumViews()-1; i >= 0; i--) {
+        PhoneView* pView = pMode->GetPhoneView(i);
+        assert(pView != NULL);
+
+        /* convert from window-relative to view-relative */
+        pHighlight = pView->FindButtonHit(event.m_x - mpViewInfo[i].GetX(),
+                                          event.m_y - mpViewInfo[i].GetY());
+        if (pHighlight != NULL) {
+            viewIndex = i;
+            break;
+        }
+    }
+
+    if (viewIndex == mpMOHViewIndex && pHighlight == mpMOHButton) {
+        /* still hovering over same button */
+    } else {
+        /* mouse has moved, possibly to a new button */
+
+        mpMOHViewIndex = viewIndex;
+        mpMOHButton = pHighlight;
+
+        /* force refresh */
+        Refresh();
+    }
+}
+
+/*
+ * Mouse has left the building.  All keys and mouse buttons up.
+ *
+ * We get one of these if the mouse moves over a child window, such as
+ * our DeviceWindow, so it is not the case that we no longer receive
+ * key input after getting this event.
+ */
+void PhoneWindow::OnMouseLeaveWindow(wxMouseEvent& WXUNUSED(event))
+{
+    //printf("--- mouse is GONE\n");
+    ClearPressedKeys();
+}
+
+/*
+ * Determine device touch screen (x,y) based on window position.
+ *
+ * Returns "true" if the click corresponds to a location on the display.
+ *
+ * TODO: should return display index as well -- currently this only
+ * supports touch on the main display.
+ */
+bool PhoneWindow::GetTouchPosition(const wxMouseEvent& event, int* pScreenX,
+    int* pScreenY)
+{
+    /*
+     * If the click came from our device window, treat it as a touch.
+     */
+    if (event.GetEventObject() != mpDeviceWindow[0])
+        return false;
+
+    *pScreenX = event.m_x;
+    *pScreenY = event.m_y;
+    return true;
+}
+
+/*
+ * We don't want to erase the background now, because it causes flicker
+ * under Windows.
+ */
+void PhoneWindow::OnErase(wxEraseEvent& WXUNUSED(event))
+{
+    //printf("erase\n");
+}
+
+/*
+ * Paint the phone and any highlighted buttons.
+ *
+ * The device output is drawn by DeviceWindow.
+ */
+void PhoneWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
+{
+    int view;
+
+    /*
+     * Under Mac OS X, the parent window is redrawn every time the child
+     * window is redrawn.  This causes poor performance in the simulator.
+     * If we're being asked to update a region that corresponds exactly
+     * to one of the device output windows, skip the redraw.
+     */
+    assert(mpViewInfo != NULL);
+    for (view = 0; view < mNumViewInfo; view++) {
+        int displayIndex;
+
+        displayIndex = mpViewInfo[view].GetDisplayIndex();
+        assert(displayIndex >= 0);
+        DeviceWindow* pDeviceWindow = mpDeviceWindow[displayIndex];
+        assert(pDeviceWindow != NULL);
+
+        wxRect displayRect = pDeviceWindow->GetRect();
+        wxRect updateRect = GetUpdateClientRect();
+
+        if (displayRect == updateRect) {
+            //printf("(skipping redraw)\n");
+            return;
+        }
+    }
+
+    wxBufferedPaintDC dc(this);
+
+    /*
+     * Erase the background to the currently-specified background color.
+     */
+    wxColour backColor = GetBackgroundColour();
+    dc.SetBrush(wxBrush(backColor));
+    dc.SetPen(wxPen(backColor, 1));
+    wxRect windowRect(wxPoint(0, 0), GetClientSize());
+    dc.DrawRectangle(windowRect);
+
+    PhoneData* pPhoneData = GetPhoneData();
+    if (pPhoneData == NULL) {
+        fprintf(stderr, "OnPaint: no phone data\n");
+        return;
+    }
+
+    PhoneMode* pPhoneMode;
+    PhoneView* pPhoneView;
+    int numImages;
+
+    pPhoneMode = pPhoneData->GetPhoneMode(GetCurrentMode().ToAscii());
+    if (pPhoneMode == NULL) {
+        fprintf(stderr, "current mode (%s) not known\n",
+            (const char*) GetCurrentMode().ToAscii());
+        return;
+    }
+
+    for (view = 0; view < pPhoneMode->GetNumViews(); view++) {
+        pPhoneView = pPhoneMode->GetPhoneView(view);
+        if (pPhoneView == NULL) {
+            fprintf(stderr, "view %d not found\n", view);
+            return;
+        }
+
+        /* draw background image and "button patches" */
+        numImages = pPhoneView->GetBkgImageCount();
+        for (int i = 0; i < numImages; i++) {
+            const LoadableImage* pLimg = pPhoneView->GetBkgImage(i);
+            wxBitmap* pBitmap = pLimg->GetBitmap();
+            if (pBitmap != NULL)
+                dc.DrawBitmap(*pBitmap,
+                    mpViewInfo[view].GetX() + pLimg->GetX(),
+                    mpViewInfo[view].GetY() + pLimg->GetY(),
+                    TRUE);
+        }
+    }
+
+
+    /*
+     * Draw button mouse-over highlight.
+     *
+     * Currently we don't do anything different when the button is held down.
+     */
+    if (mpMOHViewIndex >= 0 && mpMOHButton != NULL) {
+        // button must have graphic, or hit-testing wouldn't have worked
+        assert(mpMOHButton->GetHighlightedBitmap() != NULL);
+        dc.DrawBitmap(*mpMOHButton->GetHighlightedBitmap(),
+            mpViewInfo[mpMOHViewIndex].GetX() + mpMOHButton->GetX(),
+            mpViewInfo[mpMOHViewIndex].GetY() + mpMOHButton->GetY(),
+            TRUE);
+    }
+
+    /*
+     * Highlight pressed keys.  We want to do this in all views, because
+     * some buttons on the side of the phone might be visible in more
+     * than one view.
+     */
+    for (view = 0; view < pPhoneMode->GetNumViews(); view++) {
+        pPhoneView = pPhoneMode->GetPhoneView(view);
+        assert(pPhoneView != NULL);
+
+        ListIter iter;
+        for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) {
+            KeyCode keyCode;
+            PhoneButton* pButton;
+
+            keyCode = (*iter).GetKeyCode();
+            pButton = pPhoneView->FindButtonByKey(keyCode);
+            if (pButton != NULL) {
+                wxBitmap* pBitmap = pButton->GetSelectedBitmap();
+                if (pBitmap != NULL) {
+                    dc.DrawBitmap(*pBitmap,
+                        mpViewInfo[view].GetX() + pButton->GetX(),
+                        mpViewInfo[view].GetY() + pButton->GetY(),
+                        TRUE);
+                }
+            }
+        }
+    }
+}
+
+
+/*
+ * Press a key on the device.
+ *
+ * Schedules a screen refresh if the set of held-down keys changes.
+ */
+void PhoneWindow::AddPressedKey(KeyCode keyCode)
+{
+    /*
+     * See if the key is already down.  This usually means that the key
+     * repeat has kicked into gear.  It could also mean that we
+     * missed the key-up event, or the user has hit the same device
+     * key with both mouse and keyboard.  Either way, we don't add it
+     * a second time.  This way, if we did lose a key-up somehow, they
+     * can "clear" the stuck key by hitting it again.
+     */
+    if (keyCode == kKeyCodeUnknown) {
+        //printf("--- not adding kKeyCodeUnknown!\n");
+        return;
+    }
+
+    ListIter iter;
+    for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) {
+        if ((*iter).GetKeyCode() == keyCode)
+            break;
+    }
+    if (iter == mPressedKeys.end()) {
+        KeyInfo newInfo;
+        newInfo.SetKeyCode(keyCode);
+        mPressedKeys.push_back(newInfo);
+        //printf("---  added down=%d\n", keyCode);
+        Refresh();      // redraw w/ highlight
+    } else {
+        //printf("---  already have down=%d\n", keyCode);
+    }
+}
+
+/*
+ * Release a key on the device.
+ *
+ * Schedules a screen refresh if the set of held-down keys changes.
+ */
+void PhoneWindow::RemovePressedKey(KeyCode keyCode)
+{
+    /*
+     * Release the key.  If it's not in the list, we either missed a
+     * key-down event, or the user used both mouse and keyboard and we
+     * removed the key when the first device went up.
+     */
+    ListIter iter;
+    for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) {
+        if ((*iter).GetKeyCode() == keyCode) {
+            mPressedKeys.erase(iter);
+            //printf("---  removing down=%d\n", keyCode);
+            Refresh();      // redraw w/o highlight
+            break;
+        }
+    }
+    if (iter == mPressedKeys.end()) {
+        //printf("---  didn't find down=%d\n", keyCode);
+    }
+}
+
+/*
+ * Clear the set of keys that we think are being held down.
+ */
+void PhoneWindow::ClearPressedKeys(void)
+{
+    //printf("--- All keys up (count=%d)\n", mPressedKeys.size());
+
+    if (!mPressedKeys.empty()) {
+        ListIter iter = mPressedKeys.begin();
+        while (iter != mPressedKeys.end()) {
+            KeyCode keyCode = (*iter).GetKeyCode();
+            GetDeviceManager()->SendKeyEvent(keyCode, false);
+            iter = mPressedKeys.erase(iter);
+        }
+        Refresh();
+    }
+}
+
+/*
+ * Returns "true" if the specified key is currently pressed.
+ */
+bool PhoneWindow::IsKeyPressed(KeyCode keyCode)
+{
+    ListIter iter;
+    for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) {
+        if ((*iter).GetKeyCode() == keyCode)
+            return true;
+    }
+    return false;
+}
+
+void PhoneWindow::Vibrate(int vibrateOn)
+{
+    wxRect rect = GetRect();
+    if(vibrateOn)
+    {
+        mVibrateX = 0;
+        mTimer.Start(25);      // arg is delay in ms
+        Move(rect.x-2,rect.y);
+    }
+    else if(mTimer.IsRunning())
+    {
+        mTimer.Stop();
+        if(mVibrateX&1)
+            Move(rect.x-2,rect.y);
+        else
+            Move(rect.x+2,rect.y);
+    }
+}
+
+void PhoneWindow::OnTimer(wxTimerEvent& event)
+{
+    wxRect rect = GetRect();
+    mVibrateX++;
+    if(mVibrateX&1)
+        Move(rect.x+4,rect.y);
+    else
+        Move(rect.x-4,rect.y);
+}
diff --git a/simulator/app/PhoneWindow.h b/simulator/app/PhoneWindow.h
new file mode 100644
index 0000000..5eb1800
--- /dev/null
+++ b/simulator/app/PhoneWindow.h
@@ -0,0 +1,186 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Window with simulated phone.
+//
+#ifndef _SIM_PHONE_WINDOW_H
+#define _SIM_PHONE_WINDOW_H
+
+#include "PhoneData.h"
+#include "DeviceManager.h"
+#include "DeviceWindow.h"
+#include <ui/KeycodeLabels.h>
+
+class MainFrame;
+
+/*
+ * This window displays the simulated phone views, and handles keyboard and
+ * mouse input.
+ *
+ * If we switch to a different "mode", we may display different "views",
+ * but the set of "displays" remains the same.  (Got that?)
+ *
+ * We can't just do these things in the main frame because we can't easily
+ * grab the keyboard input.
+ */
+class PhoneWindow : public wxDialog {
+public:
+    PhoneWindow(wxWindow* parent, const wxPoint& posn);
+    virtual ~PhoneWindow(void);
+
+    /* call this initially, and after a mode change */
+    bool Setup(int phoneIdx);
+
+    bool IsReady(void) const {
+        return (mNumViewInfo > 0 && mpViewInfo != NULL);
+    }
+
+    PhoneData* GetPhoneData(void) const;
+
+    const wxString& GetCurrentMode(void) const { return mCurrentMode; }
+    void SetCurrentMode(const wxString& mode) { mCurrentMode = mode; }
+    void SetCurrentMode(const char* mode) { mCurrentMode = wxString::FromAscii(mode); }
+
+    DeviceManager* GetDeviceManager(void) { return &mDeviceManager; }
+
+    /* this is called when the phone data is reloaded */
+    void DevicesRescanned(void);
+
+    void Vibrate(int vibrateOn);
+
+private:
+    /*
+     * Hold some information about the "views" being shown in our window.
+     */
+    class ViewInfo {
+    public:
+        ViewInfo(void)
+            : mX(-1), mY(-1), mDisplayX(-1), mDisplayY(-1),
+              mWidth(-1), mHeight(-1), mDisplayIndex(-1)
+        {}
+        ~ViewInfo(void) {}
+
+        int GetX(void) const { return mX; }
+        int GetY(void) const { return mY; }
+        int GetDisplayX(void) const { return mDisplayX; }
+        int GetDisplayY(void) const { return mDisplayY; }
+        int GetWidth(void) const { return mWidth; }
+        int GetHeight(void) const { return mHeight; }
+        int GetDisplayIndex(void) const { return mDisplayIndex; }
+
+        void SetX(int val) { mX = val; }
+        void SetY(int val) { mY = val; }
+        void SetDisplayX(int val) { mDisplayX = val; }
+        void SetDisplayY(int val) { mDisplayY = val; }
+        void SetWidth(int val) { mWidth = val; }
+        void SetHeight(int val) { mHeight = val; }
+        void SetDisplayIndex(int val) { mDisplayIndex = val; }
+
+    private:
+        int     mX, mY;                 // view offset within PhoneWindow
+        int     mDisplayX, mDisplayY;   // display offset within view
+        int     mWidth, mHeight;        // view dimensions
+
+        int     mDisplayIndex;          // index into mpDeviceWindow
+    };
+
+    /*
+     * Hold information about currently pressed keys.
+     */
+    class KeyInfo {
+    public:
+        KeyInfo(void) : mKeyCode(kKeyCodeUnknown) {}
+        KeyInfo(const KeyInfo& src) {
+            mKeyCode = src.mKeyCode;
+        }
+        ~KeyInfo(void) {}
+
+        KeyInfo& operator=(const KeyInfo& src) {
+            if (this != &src) {
+                mKeyCode = src.mKeyCode;
+            }
+            return *this;
+        }
+
+        KeyCode GetKeyCode(void) const { return mKeyCode; }
+        void SetKeyCode(KeyCode keyCode) { mKeyCode = keyCode; }
+
+        //PhoneButton* GetPhoneButton(void) const { return mpButton; }
+        //void SetPhoneButton(PhoneButton* pButton) { mpButton = pButton; }
+
+    private:
+        KeyCode    mKeyCode;
+        //PhoneButton*        mpButton;
+    };
+
+    void OnActivate(wxActivateEvent& event);
+    void OnMove(wxMoveEvent& event);
+    void OnClose(wxCloseEvent& event);
+    void OnTimer(wxTimerEvent& event);
+    void OnKeyDown(wxKeyEvent& event);
+    void OnKeyUp(wxKeyEvent& event);
+    void OnErase(wxEraseEvent& event);
+    void OnPaint(wxPaintEvent& WXUNUSED(event));
+    void OnMouseLeftDown(wxMouseEvent& event);
+    void OnMouseLeftUp(wxMouseEvent& event);
+    void OnMouseRightDown(wxMouseEvent& event);
+    void OnMouseRightUp(wxMouseEvent& event);
+    void OnMouseMotion(wxMouseEvent& event);
+    void OnMouseLeaveWindow(wxMouseEvent& WXUNUSED(event));
+    bool GetTouchPosition(const wxMouseEvent& event, int* pScreenX,
+        int* pScreenY);
+
+    bool GetDimensions(PhoneData* pPhoneData, PhoneView* pPhoneView,
+        ViewInfo* pDim);
+    int ConvertKeyCode(int wxKeyCode) const;
+
+    /* press a key on the device */
+    void AddPressedKey(KeyCode keyCode);
+    /* release a key on the device */
+    void RemovePressedKey(KeyCode keyCode);
+    /* "raise" all keys */
+    void ClearPressedKeys(void);
+    /* determine whether a key is down */
+    bool IsKeyPressed(KeyCode keyCode);
+
+    /* manage the device runtime */
+    DeviceManager   mDeviceManager;
+
+    /* button mouse-over highlight handling */
+    int             mpMOHViewIndex;     // mouse is in this view
+    PhoneButton*    mpMOHButton;        //   over this button
+    KeyCode         mMouseKeySent;     // to handle "key up" for mouse button
+
+    /* handle multiple simultaneous key presses */
+    android::List<KeyInfo>  mPressedKeys;
+    typedef android::List<KeyInfo>::iterator ListIter;
+
+    /* ViewInfos, 1:1 with PhoneView entries for the current mode */
+    ViewInfo*       mpViewInfo;         // array of view data
+    int             mNumViewInfo;       // #of elements in mpViewInfo
+
+    /* DeviceWindows, 1:1 with PhoneDisplay entries for this device */
+    DeviceWindow**  mpDeviceWindow;     // array of pointers to device windows
+    int             mNumDeviceWindows;  // #of device windows
+
+    /* state */
+    int             mPhoneModel;        // index into model list
+    wxString        mCurrentMode;
+
+    bool            mPlacementChecked;  // leave it offscreen if they want
+
+    MainFrame*      mpParent;           // retain pointer to parent window
+
+    enum { kVibrateTimerId = 1010 };
+    wxTimer         mTimer;
+    int             mVibrateX;
+
+    /* touchscreen simulation */
+    bool            mTrackingTouch;
+    int             mTouchX;
+    int             mTouchY;
+
+    DECLARE_EVENT_TABLE()
+};
+
+#endif // _SIM_PHONE_WINDOW_H
diff --git a/simulator/app/Preferences.cpp b/simulator/app/Preferences.cpp
new file mode 100644
index 0000000..039f1ff
--- /dev/null
+++ b/simulator/app/Preferences.cpp
@@ -0,0 +1,475 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Preferences file access.
+//
+
+// 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"
+# include "wx/string.h"
+#endif
+
+#include "Preferences.h"
+
+#include "utils.h"
+#include "tinyxml.h"
+
+static const char* kName = "name";
+static const char* kValue = "value";
+
+
+/*
+ * Load from a file.
+ */
+bool Preferences::Load(const char* fileName)
+{
+    assert(fileName != NULL);
+    printf("SimPref: reading preferences file '%s'\n", fileName);
+
+    // throw out any existing stuff
+    delete mpDoc;
+
+    mpDoc = new TiXmlDocument;
+    if (mpDoc == NULL)
+        return false;
+
+    if (!mpDoc->LoadFile(fileName)) {
+        fprintf(stderr, "SimPref: ERROR: failed loading '%s'\n", fileName);
+        if (mpDoc->ErrorRow() != 0)
+            fprintf(stderr, "    XML: %s (row=%d col=%d)\n",
+                mpDoc->ErrorDesc(), mpDoc->ErrorRow(), mpDoc->ErrorCol());
+        else
+            fprintf(stderr, "    XML: %s\n", mpDoc->ErrorDesc());
+        goto fail;
+    }
+
+    TiXmlNode* pPrefs;
+    pPrefs = mpDoc->FirstChild("prefs");
+    if (pPrefs == NULL) {
+        fprintf(stderr, "SimPref: ERROR: could not find <prefs> in '%s'\n",
+            fileName);
+        goto fail;
+    }
+
+    // set defaults for anything we haven't set explicitly
+    SetDefaults();
+
+    return true;
+
+fail:
+    delete mpDoc;
+    mpDoc = NULL;
+    return false;
+}
+
+/*
+ * Save to a file.
+ */
+bool Preferences::Save(const char* fileName)
+{
+    assert(fileName != NULL);
+
+    if (mpDoc == NULL)
+        return false;
+
+    if (!mpDoc->SaveFile(fileName)) {
+        fprintf(stderr, "SimPref: ERROR: failed saving '%s': %s\n",
+            fileName, mpDoc->ErrorDesc());
+        return false;
+    }
+
+    mDirty = false;
+
+    return true;
+}
+
+/*
+ * Create an empty collection of preferences.
+ */
+bool Preferences::Create(void)
+{
+    static const char* docBase =
+        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
+        "<!-- Android device simulator preferences -->\n"
+        "<!-- This file is updated by the simulator -->\n"
+        "<prefs>\n"
+        "</prefs>\n";
+
+    // throw out any existing stuff
+    delete mpDoc;
+
+    // alloc and initialize
+    mpDoc = new TiXmlDocument;
+    if (mpDoc == NULL)
+        return false;
+
+    if (!mpDoc->Parse(docBase)) {
+        fprintf(stderr, "SimPref: bad docBase: %s\n", mpDoc->ErrorDesc());
+        return false;
+    }
+
+    SetDefaults();
+    mDirty = true;      // should already be, mbut make sure
+    return true;
+}
+
+/*
+ * Add default values to XML doc.
+ *
+ * This isn't strictly necessary, because the functions that are interested
+ * in the preferences can set appropriate defaults themselves when the
+ * "get" function returns "false".  However, in some cases a preference
+ * can be interesting to more than one function, and you either have to
+ * cut & paste the default value or write a "get default for xxx" function.
+ *
+ * We want this to work even if they already have an older config file, so
+ * this only sets values that don't already exist.
+ */
+void Preferences::SetDefaults(void)
+{
+    /* table of default values */
+    static const struct {
+        const char* type;
+        const char* name;
+        const char* value;
+    } kDefault[] = {
+        { "pref",           "auto-power-on",        "true" },
+        { "pref",           "debug",                "false" },
+        { "pref",           "valgrind",             "false" },
+        { "pref",           "check-jni",            "true" },
+        { "pref",           "enable-sound",         "true" },
+        { "pref",           "enable-fake-camera",   "true" },
+        { "pref",           "java-vm",              "Dalvik" },
+        /* goobuntu dapper needed LD_ASSUME_KERNEL or gdb choked badly */
+        { "pref",           "ld-assume-kernel",     "" /*2.4.19*/ },
+        { "pref",           "launch-command",
+            "xterm -geom 80x60+10+10 -sb -title Simulator -e" },
+        { "pref",           "launch-wrapper-args",  "-wait" },
+    };
+    TiXmlNode* pPrefs;
+
+    assert(mpDoc != NULL);
+
+    pPrefs = mpDoc->FirstChild("prefs");
+
+    /*
+     * Look up the name.  If it doesn't exist, add it.
+     */
+    for (int i = 0; i < NELEM(kDefault); i++) {
+        TiXmlNode* pNode = _FindNode(kDefault[i].type, kDefault[i].name);
+
+        if (pNode == NULL) {
+            TiXmlElement elem(kDefault[i].type);
+            elem.SetAttribute(kName, kDefault[i].name);
+            elem.SetAttribute(kValue, kDefault[i].value);
+            pPrefs->InsertEndChild(elem);
+
+            printf("SimPref: added default <%s> '%s'='%s'\n",
+                kDefault[i].type, kDefault[i].name, kDefault[i].value);
+        } else {
+            printf("SimPref: found existing <%s> '%s'\n", 
+                kDefault[i].type, kDefault[i].name);
+        }
+    }
+}
+
+static TiXmlNode* get_next_node(TiXmlNode* pNode)
+{
+  if (!pNode->NoChildren())
+  {
+      pNode = pNode->FirstChild();
+  }
+  else if (pNode->NoChildren() && 
+           (pNode->NextSibling() == NULL))
+  {
+      pNode = pNode->Parent()->NextSibling();
+  }
+  else
+  {
+      pNode = pNode->NextSibling();
+  }
+  return pNode;
+}
+
+/*
+ * Returns the node with element type and name specified
+ *
+ * WARNING: this searches through the tree and returns the first matching
+ * node.
+ */
+TiXmlNode* Preferences::_FindNode(const char* type, const char* str) const
+{
+    assert((type != NULL) && (str != NULL));
+    TiXmlNode* pRoot;
+    TiXmlNode* pNode;
+
+    pRoot = mpDoc->FirstChild("prefs");
+    assert(pRoot != NULL);
+
+    for (pNode = pRoot->FirstChild(); pNode != NULL;)
+    {
+        if (pNode->Type() != TiXmlNode::ELEMENT ||
+            strcasecmp(pNode->Value(), type) != 0)
+        {
+            pNode = get_next_node(pNode);
+            continue;
+        }
+
+        TiXmlElement* pElem = pNode->ToElement();
+        assert(pElem != NULL);
+
+        const char* name = pElem->Attribute(kName);
+
+        /* 1. If the name is blank, something is wrong with the config file
+         * 2. If the name matches the passed in string, we found the node
+         * 3. If the node has children, descend another level
+         * 4. If there are no children and no siblings of the node, go up a level
+         * 5. Otherwise, grab the next sibling
+         */
+        if (name == NULL)
+        {
+            fprintf(stderr, "WARNING: found <%s> without name\n", type);
+            continue;
+        }
+        else if (strcasecmp(name, str) == 0)
+        {
+            return pNode;
+        }
+        else 
+        {
+            pNode = get_next_node(pNode);
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Locate the specified preference.
+ */
+TiXmlNode* Preferences::FindPref(const char* str) const
+{
+    TiXmlNode* pNode = _FindNode("pref", str);
+    return pNode;
+}
+
+/*
+ * Like FindPref(), but returns a TiXmlElement.
+ */
+TiXmlElement* Preferences::FindPrefElement(const char* str) const
+{
+    TiXmlNode* pNode;
+
+    pNode = FindPref(str);
+    if (pNode != NULL)
+        return pNode->ToElement();
+    return NULL;
+}
+
+/*
+ * Add a new preference entry with a blank entry for value.  Returns a
+ * pointer to the new element.
+ */
+TiXmlElement* Preferences::AddPref(const char* str)
+{
+    assert(FindPref(str) == NULL);
+
+    TiXmlNode* pPrefs;
+
+    pPrefs = mpDoc->FirstChild("prefs");
+    assert(pPrefs != NULL);
+
+    TiXmlElement elem("pref");
+    elem.SetAttribute(kName, str);
+    elem.SetAttribute(kValue, "");
+    pPrefs->InsertEndChild(elem);
+
+    TiXmlNode* pNewPref = FindPref(str);
+    return pNewPref->ToElement();
+}
+
+/*
+ * Remove a node from the tree
+ */
+bool Preferences::_RemoveNode(TiXmlNode* pNode)
+{
+    if (pNode == NULL)
+        return false;
+
+    TiXmlNode* pParent = pNode->Parent();
+    if (pParent == NULL)
+        return false;
+
+    pParent->RemoveChild(pNode);
+    mDirty = true;
+    return true;
+}
+
+/*
+ * Remove a preference entry.
+ */
+bool Preferences::RemovePref(const char* delName)
+{
+    return _RemoveNode(FindPref(delName));
+}
+
+/*
+ * Test for existence.
+ */
+bool Preferences::Exists(const char* name) const
+{
+    TiXmlElement* pElem = FindPrefElement(name);
+    return (pElem != NULL);
+}
+
+/*
+ * Internal implemenations for getting values 
+ */
+bool Preferences::_GetBool(TiXmlElement* pElem, bool* pVal) const
+{
+    if (pElem != NULL) 
+    {
+        const char* str = pElem->Attribute(kValue);
+        if (str != NULL) 
+        {
+            if (strcasecmp(str, "true") == 0)
+                *pVal = true;
+            else if (strcasecmp(str, "false") == 0)
+                *pVal = false;
+            else 
+            {
+                printf("SimPref: evaluating as bool name='%s' val='%s'\n",
+                pElem->Attribute(kName), str);
+                return false;
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+bool Preferences::_GetInt(TiXmlElement* pElem, int* pInt) const
+{
+    int val;
+    if (pElem != NULL && pElem->Attribute(kValue, &val) != NULL) {
+        *pInt = val;
+        return true;
+    }
+    return false;
+}
+
+bool Preferences::_GetDouble(TiXmlElement* pElem, double* pDouble) const
+{
+    double val;
+    if (pElem != NULL && pElem->Attribute(kValue, &val) != NULL) {
+        *pDouble = val;
+        return true;
+    }
+    return false;
+}
+
+bool Preferences::_GetString(TiXmlElement* pElem, wxString& str) const
+{
+    const char* val;
+    if (pElem != NULL) {
+        val = pElem->Attribute(kValue);
+        if (val != NULL) {
+            str = wxString::FromAscii(val);
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Get a value.  Do not disturb "*pVal" unless we have something to return.
+ */
+bool Preferences::GetBool(const char* name, bool* pVal) const
+{
+    return _GetBool(FindPrefElement(name), pVal);
+}
+
+bool Preferences::GetInt(const char* name, int* pInt) const
+{
+    return _GetInt(FindPrefElement(name), pInt);
+}
+
+bool Preferences::GetDouble(const char* name, double* pDouble) const
+{
+    return _GetDouble(FindPrefElement(name), pDouble);
+}
+
+bool Preferences::GetString(const char* name, char** pVal) const
+{
+    wxString str = wxString::FromAscii(*pVal);
+    if (_GetString(FindPrefElement(name), str))
+    {
+        *pVal = android::strdupNew(str.ToAscii());
+        return true;
+    }
+    return false;   
+}
+
+bool Preferences::GetString(const char* name, wxString& str) const
+{
+    return _GetString(FindPrefElement(name), str);
+}
+
+/*
+ * Set a value.  If the preference already exists, and the value hasn't
+ * changed, don't do anything.  This avoids setting the "dirty" flag
+ * unnecessarily.
+ */
+void Preferences::SetBool(const char* name, bool val)
+{
+    bool oldVal;
+    if (GetBool(name, &oldVal) && val == oldVal)
+        return;
+
+    SetString(name, val ? "true" : "false");
+    mDirty = true;
+}
+
+void Preferences::SetInt(const char* name, int val)
+{
+    int oldVal;
+    if (GetInt(name, &oldVal) && val == oldVal)
+        return;
+
+    TiXmlElement* pElem = FindPrefElement(name);
+    if (pElem == NULL)
+        pElem = AddPref(name);
+    pElem->SetAttribute(kValue, val);
+    mDirty = true;
+}
+
+void Preferences::SetDouble(const char* name, double val)
+{
+    double oldVal;
+    if (GetDouble(name, &oldVal) && val == oldVal)
+        return;
+
+    TiXmlElement* pElem = FindPrefElement(name);
+    if (pElem == NULL)
+        pElem = AddPref(name);
+    pElem->SetDoubleAttribute(kValue, val);
+    mDirty = true;
+}
+
+void Preferences::SetString(const char* name, const char* val)
+{
+    wxString oldVal;
+    if (GetString(name, /*ref*/oldVal) && strcmp(oldVal.ToAscii(), val) == 0)
+        return;
+
+    TiXmlElement* pElem = FindPrefElement(name);
+    if (pElem == NULL)
+        pElem = AddPref(name);
+    pElem->SetAttribute(kValue, val);
+    mDirty = true;
+}
+
+
diff --git a/simulator/app/Preferences.h b/simulator/app/Preferences.h
new file mode 100644
index 0000000..91c35de
--- /dev/null
+++ b/simulator/app/Preferences.h
@@ -0,0 +1,104 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Preferences file access.
+//
+#ifndef _SIM_PREFERENCES_H
+#define _SIM_PREFERENCES_H
+
+#include "tinyxml.h"
+
+/*
+ * This class provides access to a preferences file.  It's possible to
+ * have more than one instance, though it's probably unwise to have multiple
+ * objects for the same file on disk.
+ *
+ * All value are stored as strings.  The class doesn't really try to
+ * enforce type safety, but it will complain if you try to do something
+ * nonsensical (like convert "foo" to an integer).
+ */
+class Preferences {
+public:
+    Preferences(void) :
+        mpDoc(NULL), mDirty(false)
+        {}
+    ~Preferences(void) {
+        delete mpDoc;
+    }
+
+    /* load all preferences from a file */
+    bool Load(const char* fileName);
+
+    /* save all preferences to a file */
+    bool Save(const char* fileName);
+
+    /* create new preferences set (use when file does not exist) */
+    bool Create(void);
+
+    /*
+     * Retrieve a value from the preferences database.
+     *
+     * These return "false" if the value was not found or could not be
+     * converted to the expected type.  The value pointed to be the second
+     * arg is guaranteed to be left undisturbed in this case.
+     *
+     * The value set by GetString(const char*, char**) will be set to
+     * newly-allocated storage that must be freed with "delete[]".  This
+     * is done instead of returning a const char* because it's unclear
+     * what guarantees TinyXml makes wrt string lifetime (especially in
+     * a multithreaded environment).
+     */
+    bool GetBool(const char* name, bool* pVal) const;
+    bool GetInt(const char* name, int* pInt) const;
+    bool GetDouble(const char* name, double* pDouble) const;
+    bool GetString(const char* name, char** pVal) const;
+    bool GetString(const char* name, wxString& str) const;
+
+    /*
+     * Set a value in the database.
+     */
+    void SetBool(const char* name, bool val);
+    void SetInt(const char* name, int val);
+    void SetDouble(const char* name, double val);
+    void SetString(const char* name, const char* val);
+
+    /*
+     * Just test for existence.
+     */
+    bool Exists(const char* name) const;
+    
+    /*
+     * Remove a <pref> from the config file.
+     */
+    bool RemovePref(const char* name);
+    
+    /*
+     * Get the value of the "dirty" flag.
+     */
+    bool GetDirty(void) const { return mDirty; }
+
+private:
+    /* Implementation of getters */
+    bool _GetBool(TiXmlElement* pElem, bool* pVal) const;
+    bool _GetInt(TiXmlElement* pElem, int* pInt) const;
+    bool _GetDouble(TiXmlElement* pElem, double* pDouble) const;
+    bool _GetString(TiXmlElement* pElem, wxString& str) const;
+    
+    /* this can be used to generate some defaults */
+    void SetDefaults(void);
+
+    /* locate the named preference */
+    TiXmlNode* _FindNode(const char* type, const char* name) const;
+    TiXmlNode* FindPref(const char* str) const;
+    /* like FindPref, but returns a TiXmlElement */
+    TiXmlElement* FindPrefElement(const char* str) const;
+    /* add a new preference entry */
+    TiXmlElement* AddPref(const char* str);
+    /* removes a node */
+    bool _RemoveNode(TiXmlNode* pNode);
+    
+    TiXmlDocument*  mpDoc;
+    bool            mDirty;
+};
+
+#endif // _SIM_PREFERENCES_H
diff --git a/simulator/app/PrefsDialog.cpp b/simulator/app/PrefsDialog.cpp
new file mode 100644
index 0000000..e146a56
--- /dev/null
+++ b/simulator/app/PrefsDialog.cpp
@@ -0,0 +1,292 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Preferences modal dialog.
+//
+
+// 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 "PrefsDialog.h"
+#include "Preferences.h"
+#include "MyApp.h"
+#include "Resource.h"
+
+BEGIN_EVENT_TABLE(PrefsDialog, wxDialog)
+END_EVENT_TABLE()
+
+/*
+ * Constructor.
+ */
+PrefsDialog::PrefsDialog(wxWindow* parent)
+    : wxDialog(parent, IDD_PREFS, wxT("Preferences"), wxDefaultPosition,
+        wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
+      mAutoPowerOn(false),
+      mGammaCorrection(1.0),
+      mEnableSound(true),
+      mEnableFakeCamera(true),
+      mLogLevel(0)
+{
+    LoadPreferences();   
+    CreateControls();
+}
+
+/*
+ * Destructor.  Not much to do.
+ */
+PrefsDialog::~PrefsDialog()
+{
+}
+
+/*
+ * Create all of the pages and add them to the notebook.
+ */
+void PrefsDialog::CreateControls(void)
+{
+    wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
+    wxBoxSizer* okCancelSizer = new wxBoxSizer(wxHORIZONTAL);
+    mNotebook.Create(this, wxID_ANY);
+    wxPanel* page;
+
+    /* pages added to notebook are owned by notebook */
+    page = CreateSimulatorPage(&mNotebook);
+    mNotebook.AddPage(page, wxT("Simulator"), true);       // selected page
+    page = CreateRuntimePage(&mNotebook);
+    mNotebook.AddPage(page, wxT("Runtime"), false);
+
+    wxButton* cancel = new wxButton(this, wxID_CANCEL, wxT("&Cancel"),
+        wxDefaultPosition, wxDefaultSize, 0);
+    okCancelSizer->Add(cancel, 0, wxALL | wxALIGN_RIGHT, kInterSpacing);
+
+    wxButton* ok = new wxButton(this, wxID_OK, wxT("&OK"),
+        wxDefaultPosition, wxDefaultSize, 0);
+    okCancelSizer->Add(ok, 0, wxALL | wxALIGN_RIGHT, kInterSpacing);
+
+    mainSizer->Add(&mNotebook, 1, wxEXPAND);
+    mainSizer->Add(okCancelSizer, 0, wxALIGN_RIGHT);
+
+    SetSizer(mainSizer);
+
+    mainSizer->Fit(this);           // shrink-to-fit
+    mainSizer->SetSizeHints(this);  // define minimum size
+}
+
+/*
+ * Load preferences from config file
+ */
+void PrefsDialog::LoadPreferences(void)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    /*
+     * Load preferences.
+     */
+    mConfigFile = ((MyApp*)wxTheApp)->GetConfigFileName();
+
+    pPrefs->GetDouble("gamma", &mGammaCorrection);
+    pPrefs->GetString("debugger", /*ref*/ mDebugger);
+    pPrefs->GetString("valgrinder", /*ref*/ mValgrinder);
+    pPrefs->GetBool("auto-power-on", &mAutoPowerOn);
+    pPrefs->GetBool("enable-sound", &mEnableSound);
+    pPrefs->GetBool("enable-fake-camera", &mEnableFakeCamera);
+}
+
+/*
+ * Transfer data from our members to the window controls.
+ *
+ * First we have to pull the data out of the preferences database.
+ * Anything that hasn't already been added with a default value will
+ * be given a default here, which may or may not match the default
+ * behavior elsewhere.  The best solution to this is to define the
+ * default when the preferences file is created or read, so that we're
+ * never left guessing here.
+ */
+bool PrefsDialog::TransferDataToWindow(void)
+{
+    /*
+     * Do standard dialog setup.
+     */
+    wxTextCtrl* configFileName = (wxTextCtrl*) FindWindow(IDC_SPREFS_CONFIG_NAME);
+    wxTextCtrl* debugger = (wxTextCtrl*) FindWindow(IDC_SPREFS_DEBUGGER);
+    wxTextCtrl* valgrinder = (wxTextCtrl*) FindWindow(IDC_SPREFS_VALGRINDER);
+    wxCheckBox* autoPowerOn = (wxCheckBox*) FindWindow(IDC_SPREFS_AUTO_POWER_ON);
+    wxCheckBox* enableSound = (wxCheckBox*) FindWindow(IDC_RPREFS_ENABLE_SOUND);
+    wxCheckBox* enableFakeCamera = (wxCheckBox*) FindWindow(IDC_RPREFS_ENABLE_FAKE_CAMERA);
+
+    wxTextCtrl* gamma = (wxTextCtrl*) FindWindow(IDC_RPREFS_GAMMA);
+
+    configFileName->SetValue(mConfigFile);
+    debugger->SetValue(mDebugger);
+    valgrinder->SetValue(mValgrinder);
+    autoPowerOn->SetValue(mAutoPowerOn);
+    enableSound->SetValue(mEnableSound);
+    enableFakeCamera->SetValue(mEnableFakeCamera);
+
+    wxString tmpStr;
+    tmpStr.Printf(wxT("%.3f"), mGammaCorrection);
+    gamma->SetValue(tmpStr);
+
+    return true;
+}
+
+/*
+ * Transfer and validate data from the window controls.
+ *
+ * This doesn't get called if the user cancels out of the dialog.
+ */
+bool PrefsDialog::TransferDataFromControls(void)
+{
+    /*
+     * Do standard dialog export.
+     *
+     * We should error-check all of these.
+     */
+    // configName is read-only, don't need it here
+    wxTextCtrl* debugger = (wxTextCtrl*) FindWindow(IDC_SPREFS_DEBUGGER);
+    wxTextCtrl* valgrinder = (wxTextCtrl*) FindWindow(IDC_SPREFS_VALGRINDER);
+    wxCheckBox* autoPowerOn = (wxCheckBox*) FindWindow(IDC_SPREFS_AUTO_POWER_ON);
+    wxCheckBox* enableSound = (wxCheckBox*) FindWindow(IDC_RPREFS_ENABLE_SOUND);
+    wxCheckBox* enableFakeCamera = (wxCheckBox*) FindWindow(IDC_RPREFS_ENABLE_FAKE_CAMERA);
+
+    wxTextCtrl* gamma = (wxTextCtrl*) FindWindow(IDC_RPREFS_GAMMA);
+
+    mDebugger = debugger->GetValue();
+    mValgrinder = valgrinder->GetValue();
+    mAutoPowerOn = autoPowerOn->GetValue();
+    mEnableSound = enableSound->GetValue();
+    mEnableFakeCamera = enableFakeCamera->GetValue();
+
+    wxString tmpStr;
+    tmpStr = gamma->GetValue();
+    bool toDouble = tmpStr.ToDouble(&mGammaCorrection);    // returns 0.0 on err; use strtof()?
+
+    if (!toDouble || mGammaCorrection <= 0.0 || mGammaCorrection > 2.0) {
+        wxMessageBox(wxT("Bad value for gamma -- must be > 0.0 and <= 2.0"),
+            wxT("Hoser"), wxOK, this);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Transfer preferences to config file
+ */
+bool PrefsDialog::TransferDataFromWindow(void)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    /*
+     * Grab the information from the controls and save in member field
+     */
+
+    if (!TransferDataFromControls())
+        return false;
+
+    pPrefs->SetString("debugger", mDebugger.ToAscii());
+    pPrefs->SetString("valgrinder", mValgrinder.ToAscii());
+    pPrefs->SetBool("auto-power-on", mAutoPowerOn);
+    pPrefs->SetBool("enable-sound", mEnableSound);
+    pPrefs->SetBool("enable-fake-camera", mEnableFakeCamera);
+
+    pPrefs->SetDouble("gamma", mGammaCorrection);
+
+    return true;
+}
+
+
+/*
+ * Create the Simulator Preferences page.
+ */
+wxPanel* PrefsDialog::CreateSimulatorPage(wxBookCtrlBase* parent)
+{
+    wxPanel* panel = new wxPanel(parent);
+
+    wxStaticText* configNameDescr = new wxStaticText(panel, wxID_STATIC,
+        wxT("Config file:"));
+    wxTextCtrl* configName = new wxTextCtrl(panel, IDC_SPREFS_CONFIG_NAME,
+        wxT(""), wxDefaultPosition, wxDefaultSize, wxTE_READONLY);
+    // make it visibly different; unfortunately this kills scroll, copy&paste
+    configName->Enable(false);
+
+    wxStaticText* debuggerDescr = new wxStaticText(panel, wxID_STATIC,
+        wxT("Debugger:"));
+    wxTextCtrl* debugger = new wxTextCtrl(panel, IDC_SPREFS_DEBUGGER);
+
+    wxStaticText* valgrinderDescr = new wxStaticText(panel, wxID_STATIC,
+        wxT("Valgrind:"));
+    wxTextCtrl* valgrinder = new wxTextCtrl(panel, IDC_SPREFS_VALGRINDER);
+
+    wxCheckBox* autoPowerOn = new wxCheckBox(panel, IDC_SPREFS_AUTO_POWER_ON,
+        wxT("Boot runtime when simulator starts"));
+
+    wxBoxSizer* sizerPanel = new wxBoxSizer(wxVERTICAL);
+    sizerPanel->Add(kMinWidth, kEdgeSpacing);       // forces minimum width
+    sizerPanel->Add(configNameDescr);
+    sizerPanel->Add(configName, 0, wxEXPAND);
+    sizerPanel->AddSpacer(kInterSpacing);
+    sizerPanel->AddSpacer(kInterSpacing);
+    sizerPanel->Add(debuggerDescr);
+    sizerPanel->Add(debugger, 0, wxEXPAND);
+    sizerPanel->AddSpacer(kInterSpacing);
+    sizerPanel->Add(valgrinderDescr);
+    sizerPanel->Add(valgrinder, 0, wxEXPAND);
+    sizerPanel->AddSpacer(kInterSpacing);
+    sizerPanel->Add(autoPowerOn);
+    sizerPanel->AddSpacer(kInterSpacing);
+
+    wxBoxSizer* horizIndent = new wxBoxSizer(wxHORIZONTAL);
+    horizIndent->AddSpacer(kEdgeSpacing);
+    horizIndent->Add(sizerPanel, wxSHAPED);
+    horizIndent->AddSpacer(kEdgeSpacing);
+    panel->SetSizer(horizIndent);
+
+    return panel;
+}
+
+/*
+ * Create the Runtime Preferences page.
+ */
+wxPanel* PrefsDialog::CreateRuntimePage(wxBookCtrlBase* parent)
+{
+    wxPanel* panel = new wxPanel(parent);
+
+    wxStaticText* gammaStrDescr = new wxStaticText(panel, wxID_STATIC,
+        wxT("Gamma correction:"));
+    wxTextCtrl* gammaStr = new wxTextCtrl(panel, IDC_RPREFS_GAMMA);
+
+    wxBoxSizer* gammaSizer = new wxBoxSizer(wxHORIZONTAL);
+    gammaSizer->Add(gammaStrDescr, 0, wxALIGN_CENTER_VERTICAL);
+    gammaSizer->AddSpacer(kInterSpacing);
+    gammaSizer->Add(gammaStr);
+
+    wxBoxSizer* sizerPanel = new wxBoxSizer(wxVERTICAL);
+    sizerPanel->Add(kMinWidth, kEdgeSpacing);       // forces minimum width
+    sizerPanel->Add(gammaSizer);
+    sizerPanel->AddSpacer(kInterSpacing);
+
+    wxCheckBox* enableSound = new wxCheckBox(panel, IDC_RPREFS_ENABLE_SOUND,
+        wxT("Enable Sound"));
+    sizerPanel->AddSpacer(kInterSpacing);
+    sizerPanel->Add(enableSound);
+
+    wxCheckBox* enableFakeCamera = new wxCheckBox(panel, IDC_RPREFS_ENABLE_FAKE_CAMERA,
+        wxT("Enable Fake Camera"));
+    sizerPanel->AddSpacer(kInterSpacing);
+    sizerPanel->Add(enableFakeCamera);
+
+    wxBoxSizer* horizIndent = new wxBoxSizer(wxHORIZONTAL);
+    horizIndent->AddSpacer(kEdgeSpacing);
+    horizIndent->Add(sizerPanel, wxEXPAND);
+    horizIndent->AddSpacer(kEdgeSpacing);
+    panel->SetSizer(horizIndent);
+
+    return panel;
+}
+
diff --git a/simulator/app/PrefsDialog.h b/simulator/app/PrefsDialog.h
new file mode 100644
index 0000000..250f79e
--- /dev/null
+++ b/simulator/app/PrefsDialog.h
@@ -0,0 +1,54 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Prefs modal dialog.
+//
+#ifndef _SIM_PREFS_DIALOG_H
+#define _SIM_PREFS_DIALOG_H
+
+/*
+ * Declaration of preferences dialog.  This class defines the outer
+ * wrapper as well as all of the pages.
+ */
+class PrefsDialog : public wxDialog {
+    //DECLARE_CLASS(PrefsDialog)    // shown in book, but causes link problems
+    DECLARE_EVENT_TABLE()
+
+public:
+    PrefsDialog(wxWindow* parent);
+    virtual ~PrefsDialog();
+
+    void CreateControls(void);
+
+    wxString    mConfigFile;
+
+private:
+    bool TransferDataToWindow(void);
+    bool TransferDataFromWindow(void);
+    bool TransferDataFromControls(void);
+    void LoadPreferences(void);
+    
+    wxPanel* CreateSimulatorPage(wxBookCtrlBase* parent);
+    wxPanel* CreateRuntimePage(wxBookCtrlBase* parent);
+
+    /* main notebook; for aesthetic reasons we may want a Choicebook */
+    wxNotebook    mNotebook;
+
+    /* Global simulator options */
+    wxString    mDebugger;
+    wxString    mValgrinder;
+    bool        mAutoPowerOn;
+    // log window size?
+
+    /* Global runtime options */
+    double      mGammaCorrection;
+    bool        mEnableSound;
+    bool        mEnableFakeCamera;
+    int         mLogLevel;
+
+    enum {
+        kMinWidth = 300,        // minimum prefs dialog width, in pixels
+    };
+};
+
+#endif // _SIM_PREFS_DIALOG_H
diff --git a/simulator/app/PropertyServer.cpp b/simulator/app/PropertyServer.cpp
new file mode 100644
index 0000000..ee17657
--- /dev/null
+++ b/simulator/app/PropertyServer.cpp
@@ -0,0 +1,465 @@
+//
+// Copyright 2007 The Android Open Source Project
+//
+// Property sever.  Mimics behavior provided on the device by init(8) and
+// some code built into libc.
+//
+    
+// 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 "PropertyServer.h"
+#include "MyApp.h"
+#include "Preferences.h"
+#include "MainFrame.h"
+#include "utils.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+
+using namespace android;
+
+const char* PropertyServer::kPropCheckJni = "ro.kernel.android.checkjni";
+
+/*
+ * Destructor.
+ */
+PropertyServer::~PropertyServer(void)
+{
+    if (IsRunning()) {
+        // TODO: cause thread to stop, then Wait for it
+    }
+    printf("Sim: in ~PropertyServer()\n");
+}
+
+/*
+ * Create and run the thread.
+ */
+bool PropertyServer::StartThread(void)
+{
+    if (Create() != wxTHREAD_NO_ERROR) {
+        fprintf(stderr, "Sim: ERROR: can't create PropertyServer thread\n");
+        return false;
+    }
+
+    Run();
+    return true;
+}
+
+
+/*
+ * Clear out the list.
+ */
+void PropertyServer::ClearProperties(void)
+{
+    typedef List<Property>::iterator PropIter;
+
+    for (PropIter pi = mPropList.begin(); pi != mPropList.end(); ++pi) {
+        pi = mPropList.erase(pi);
+    }
+}
+
+/*
+ * Set default values for several properties.
+ */
+void PropertyServer::SetDefaultProperties(void)
+{
+    static const struct {
+        const char* key;
+        const char* value;
+    } propList[] = {
+        { "net.bt.name", "Android" },
+        { "ro.kernel.mem", "60M" },
+        { "ro.kernel.board_sardine.version", "4" },
+        { "ro.kernel.console", "null" },
+        { "ro.build.id", "engineering" },
+        { "ro.build.date", "Wed Nov 28 07:44:14 PST 2007" },
+        { "ro.build.date.utc", "1196264654" },
+        { "ro.build.type", "eng" },
+        { "ro.product.device", "simulator" /*"sooner"*/ },
+        { "ro.product.brand", "generic" },
+        { "ro.build.user", "fadden" },
+        { "ro.build.host", "marathon" },
+        { "ro.config.nocheckin", "yes" },
+        { "ro.product.manufacturer", "" },
+        { "ro.radio.use-ppp", "no" },
+        { "ro.FOREGROUND_APP_ADJ", "0" },
+        { "ro.VISIBLE_APP_ADJ", "1" },
+        { "ro.SECONDARY_SERVER_ADJ", "2" },
+        { "ro.HIDDEN_APP_MIN_ADJ", "7" },
+        { "ro.CONTENT_PROVIDER_ADJ", "14" },
+        { "ro.EMPTY_APP_ADJ", "15" },
+        { "ro.FOREGROUND_APP_MEM", "1536" },
+        { "ro.VISIBLE_APP_MEM", "2048" },
+        { "ro.SECONDARY_SERVER_MEM", "4096" },
+        { "ro.HIDDEN_APP_MEM", "8192" },
+        { "ro.EMPTY_APP_MEM", "16384" },
+        //{ "init.svc.adbd", "running" },       // causes ADB-JDWP
+        { "init.svc.usbd", "running" },
+        { "init.svc.debuggerd", "running" },
+        { "init.svc.ril-daemon", "running" },
+        { "init.svc.zygote", "running" },
+        { "init.svc.runtime", "running" },
+        { "init.svc.dbus", "running" },
+        { "init.svc.pppd_gprs", "running" },
+        { "adb.connected", "0" },
+        //{ "use-adb-networking", "1" },
+        /*
+        { "status.battery.state", "Slow" },
+        { "status.battery.level", "5" },
+        { "status.battery.level_raw", "50" },
+        { "status.battery.level_scale", "9" },
+        */
+
+        /* disable the annoying setup wizard */
+        { "app.setupwizard.disable", "1" },
+
+        /* Dalvik options, set by AndroidRuntime */
+        { "dalvik.vm.execution-mode", "int:portable" },
+        { "dalvik.vm.enableassertions", "all" },    // -ea
+        { "dalvik.vm.verify-bytecode", "false" },   // -Xverify
+        { "dalvik.vm.deadlock-predict", "off" },    // -Xdeadlockpredict
+        { "log.redirect-stdio", "false" },          // -Xlog-stdio
+
+        /* SurfaceFlinger options */
+        { "debug.sf.nobootanimation", "1" },
+        { "debug.sf.showupdates", "0" },
+        { "debug.sf.showcpu", "0" },
+        { "debug.sf.showbackground", "0" },
+        { "debug.sf.showfps", "0" },
+    };
+
+    for (int i = 0; i < NELEM(propList); i++)
+        SetProperty(propList[i].key, propList[i].value);
+
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    bool doCheckJni = false;
+
+    pPrefs->GetBool("check-jni", &doCheckJni);
+    if (doCheckJni)
+        SetProperty(kPropCheckJni, "1");
+    else
+        SetProperty(kPropCheckJni, "0");
+}
+
+/*
+ * Get the value of a property.
+ *
+ * "valueBuf" must hold at least PROPERTY_VALUE_MAX bytes.
+ *
+ * Returns "true" if the property was found.
+ */
+bool PropertyServer::GetProperty(const char* key, char* valueBuf)
+{
+    typedef List<Property>::iterator PropIter;
+
+    assert(key != NULL);
+    assert(valueBuf != NULL);
+
+    for (PropIter pi = mPropList.begin(); pi != mPropList.end(); ++pi) {
+        Property& prop = *pi;
+        if (strcmp(prop.key, key) == 0) {
+            if (strlen(prop.value) >= PROPERTY_VALUE_MAX) {
+                fprintf(stderr,
+                    "GLITCH: properties table holds '%s' '%s' (len=%d)\n",
+                    prop.key, prop.value, strlen(prop.value));
+                abort();
+            }
+            assert(strlen(prop.value) < PROPERTY_VALUE_MAX);
+            strcpy(valueBuf, prop.value);
+            return true;
+        }
+    }
+
+    //printf("Prop: get [%s] not found\n", key);
+    return false;
+}
+
+/*
+ * Set the value of a property, replacing it if it already exists.
+ *
+ * If "value" is NULL, the property is removed.
+ *
+ * If the property is immutable, this returns "false" without doing
+ * anything.  (Not implemented.)
+ */
+bool PropertyServer::SetProperty(const char* key, const char* value)
+{
+    typedef List<Property>::iterator PropIter;
+
+    assert(key != NULL);
+    assert(value != NULL);
+
+    for (PropIter pi = mPropList.begin(); pi != mPropList.end(); ++pi) {
+        Property& prop = *pi;
+        if (strcmp(prop.key, key) == 0) {
+            if (value != NULL) {
+                //printf("Prop: replacing [%s]: [%s] with [%s]\n",
+                //    prop.key, prop.value, value);
+                strcpy(prop.value, value);
+            } else {
+                //printf("Prop: removing [%s]\n", prop.key);
+                mPropList.erase(pi);
+            }
+            return true;
+        }
+    }
+
+    //printf("Prop: adding [%s]: [%s]\n", key, value);
+    Property tmp;
+    strcpy(tmp.key, key);
+    strcpy(tmp.value, value);
+    mPropList.push_back(tmp);
+    return true;
+}
+
+/*
+ * Create a UNIX domain socket, carefully removing it if it already
+ * exists.
+ */
+bool PropertyServer::CreateSocket(const char* fileName)
+{
+    struct stat sb;
+    bool result = false;
+    int sock = -1;
+    int cc;
+
+    cc = stat(fileName, &sb);
+    if (cc < 0) {
+        if (errno != ENOENT) {
+            LOG(LOG_ERROR, "sim-prop",
+                "Unable to stat '%s' (errno=%d)\n", fileName, errno);
+            goto bail;
+        }
+    } else {
+        /* don't touch it if it's not a socket */
+        if (!(S_ISSOCK(sb.st_mode))) {
+            LOG(LOG_ERROR, "sim-prop",
+                "File '%s' exists and is not a socket\n", fileName);
+            goto bail;
+        }
+
+        /* remove the cruft */
+        if (unlink(fileName) < 0) {
+            LOG(LOG_ERROR, "sim-prop",
+                "Unable to remove '%s' (errno=%d)\n", fileName, errno);
+            goto bail;
+        }
+    }
+
+    struct sockaddr_un addr;
+
+    sock = ::socket(AF_UNIX, SOCK_STREAM, 0);
+    if (sock < 0) {
+        LOG(LOG_ERROR, "sim-prop",
+            "UNIX domain socket create failed (errno=%d)\n", errno);
+        goto bail;
+    }
+
+    /* bind the socket; this creates the file on disk */
+    strcpy(addr.sun_path, fileName);    // max 108 bytes
+    addr.sun_family = AF_UNIX;
+    cc = ::bind(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
+    if (cc < 0) {
+        LOG(LOG_ERROR, "sim",
+            "AF_UNIX bind failed for '%s' (errno=%d)\n", fileName, errno);
+        goto bail;
+    }
+
+    cc = ::listen(sock, 5);
+    if (cc < 0) {
+        LOG(LOG_ERROR, "sim", "AF_UNIX listen failed (errno=%d)\n", errno);
+        goto bail;
+    }
+
+    mListenSock = sock;
+    sock = -1;
+    result = true;
+
+bail:
+    if (sock >= 0)
+        close(sock);
+    return result;
+}
+
+/*
+ * Handle a client request.
+ *
+ * Returns true on success, false if the fd should be closed.
+ */
+bool PropertyServer::HandleRequest(int fd)
+{
+    char reqBuf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX];
+    char valueBuf[1 + PROPERTY_VALUE_MAX];
+    ssize_t actual;
+
+    memset(valueBuf, 'x', sizeof(valueBuf));        // placate valgrind
+
+    /* read the command byte; this determines the message length */
+    actual = read(fd, reqBuf, 1);
+    if (actual <= 0)
+        return false;
+
+    if (reqBuf[0] == kSystemPropertyGet) {
+        actual = read(fd, reqBuf, PROPERTY_KEY_MAX);
+
+        if (actual != PROPERTY_KEY_MAX) {
+            fprintf(stderr, "Bad read on get: %d of %d\n",
+                actual, PROPERTY_KEY_MAX);
+            return false;
+        }
+        if (GetProperty(reqBuf, valueBuf+1))
+            valueBuf[0] = 1;
+        else
+            valueBuf[0] = 0;
+        //printf("GET property [%s]: (found=%d) [%s]\n",
+        //    reqBuf, valueBuf[0], valueBuf+1);
+        if (write(fd, valueBuf, sizeof(valueBuf)) != sizeof(valueBuf)) {
+            fprintf(stderr, "Bad write on get\n");
+            return false;
+        }
+    } else if (reqBuf[0] == kSystemPropertySet) {
+        actual = read(fd, reqBuf, PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX);
+        if (actual != PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX) {
+            fprintf(stderr, "Bad read on set: %d of %d\n",
+                actual, PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX);
+            return false;
+        }
+        //printf("SET property '%s'\n", reqBuf);
+        if (SetProperty(reqBuf, reqBuf + PROPERTY_KEY_MAX))
+            valueBuf[0] = 1;
+        else
+            valueBuf[0] = 0;
+        if (write(fd, valueBuf, 1) != 1) {
+            fprintf(stderr, "Bad write on set\n");
+            return false;
+        }
+    } else if (reqBuf[0] == kSystemPropertyList) {
+        /* TODO */
+        assert(false);
+    } else {
+        fprintf(stderr, "Unexpected request %d from prop client\n", reqBuf[0]);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Serve up properties.
+ */
+void PropertyServer::ServeProperties(void)
+{
+    typedef List<int>::iterator IntIter;
+    fd_set readfds;
+    int maxfd;
+
+    while (true) {
+        int cc;
+
+        FD_ZERO(&readfds);
+        FD_SET(mListenSock, &readfds);
+        maxfd = mListenSock;
+
+        for (IntIter ii = mClientList.begin(); ii != mClientList.end(); ++ii) {
+            int fd = (*ii);
+
+            FD_SET(fd, &readfds);
+            if (maxfd < fd)
+                maxfd = fd;
+        }
+
+        cc = select(maxfd+1, &readfds, NULL, NULL, NULL);
+        if (cc < 0) {
+            if (errno == EINTR) {
+                printf("hiccup!\n");
+                continue;
+            }
+            return;
+        }
+        if (FD_ISSET(mListenSock, &readfds)) {
+            struct sockaddr_un from;
+            socklen_t fromlen;
+            int newSock;
+
+            fromlen = sizeof(from);
+            newSock = ::accept(mListenSock, (struct sockaddr*) &from, &fromlen);
+            if (newSock < 0) {
+                LOG(LOG_WARN, "sim",
+                    "AF_UNIX accept failed (errno=%d)\n", errno);
+            } else {
+                //printf("new props connection on %d --> %d\n",
+                //    mListenSock, newSock);
+
+                mClientList.push_back(newSock);
+            }
+        }
+
+        for (IntIter ii = mClientList.begin(); ii != mClientList.end(); ) {
+            int fd = (*ii);
+            bool ok = true;
+
+            if (FD_ISSET(fd, &readfds)) {
+                //printf("--- activity on %d\n", fd);
+
+                ok = HandleRequest(fd);
+            }
+
+            if (ok) {
+                ++ii;
+            } else {
+                //printf("--- closing %d\n", fd);
+                close(fd);
+                ii = mClientList.erase(ii);
+            }
+        }
+    }
+}
+
+/*
+ * Thread entry point.
+ *
+ * This just sits and waits for a new connection.  It hands it off to the
+ * main thread and then goes back to waiting.
+ *
+ * There is currently no "polite" way to shut this down.
+ */
+void* PropertyServer::Entry(void)
+{
+    if (CreateSocket(SYSTEM_PROPERTY_PIPE_NAME)) {
+        assert(mListenSock >= 0);
+        SetDefaultProperties();
+
+        /* loop until it's time to exit or we fail */
+        ServeProperties();
+
+        ClearProperties();
+
+        /*
+         * Close listen socket and all clients.
+         */
+        LOG(LOG_INFO, "sim", "Cleaning up socket list\n");
+        typedef List<int>::iterator IntIter;
+        for (IntIter ii = mClientList.begin(); ii != mClientList.end(); ++ii)
+            close((*ii));
+        close(mListenSock);
+    }
+
+    LOG(LOG_INFO, "sim", "PropertyServer thread exiting\n");
+    return NULL;
+}
+
diff --git a/simulator/app/PropertyServer.h b/simulator/app/PropertyServer.h
new file mode 100644
index 0000000..193dd70
--- /dev/null
+++ b/simulator/app/PropertyServer.h
@@ -0,0 +1,68 @@
+//
+// Copyright 2007 The Android Open Source Project
+//
+// Serve properties to the simulated runtime.
+//
+#ifndef _SIM_PROPERTY_SERVER_H
+#define _SIM_PROPERTY_SERVER_H
+
+#include "cutils/properties.h"
+#include "utils/List.h"
+
+/*
+ * Define a thread that responds to requests from clients to get/set/list
+ * system properties.
+ */
+class PropertyServer : public wxThread {
+public:
+    PropertyServer(void) : mListenSock(-1) {}
+    virtual ~PropertyServer(void);
+
+    /* start the thread running */
+    bool StartThread(void);
+
+    /* thread entry point */
+    virtual void* Entry(void);
+
+    /* clear out all properties */
+    void ClearProperties(void);
+
+    /* add some default values */
+    void SetDefaultProperties(void);
+
+    /* copy a property into valueBuf; returns false if property not found */
+    bool GetProperty(const char* key, char* valueBuf);
+
+    /* set the property, replacing it if it already exists */
+    bool SetProperty(const char* key, const char* value);
+
+    /* property name constants */
+    static const char* kPropCheckJni;
+
+private:
+    /* one property entry */
+    typedef struct Property {
+        char    key[PROPERTY_KEY_MAX];
+        char    value[PROPERTY_VALUE_MAX];
+    } Property;
+
+    /* create the UNIX-domain socket we listen on */
+    bool CreateSocket(const char* fileName);
+
+    /* serve up properties */
+    void ServeProperties(void);
+
+    /* handle a client request */
+    bool HandleRequest(int fd);
+
+    /* listen here for new connections */
+    int     mListenSock;
+
+    /* list of connected fds to scan */
+    android::List<int>      mClientList;
+
+    /* set of known properties */
+    android::List<Property> mPropList;
+};
+
+#endif // PROPERTY_SERVER_H
diff --git a/simulator/app/README b/simulator/app/README
new file mode 100644
index 0000000..5a51f63
--- /dev/null
+++ b/simulator/app/README
@@ -0,0 +1,74 @@
+Android Simulator README
+Last updated: 14-Nov-2007
+
+See "docs/sim-layout-xml.txt" for a description of the layout.xml files
+in the device directories.
+
+The coding conventions here are generally aligned with wxWidgets' style,
+which is similar to standard Windows style.  The only significant shift
+from Android style is that function names are capitalized.  Note the
+simulator code is not part of the "android" namespace.
+
+
+===== Arguments =====
+
+The config file for the simulator, ".android.cf", can live in your
+$HOME directory or in $cwd.  The copy in $cwd takes priority.  If a
+config file does not exist, one will be created in your home directory.
+(Note that the current directory is set by "runsim.sh", so if you launch
+the simulator from the script it will look for the config file in your
+"install" directory.)
+
+The simulator takes the following optional arguments:
+
+ -f <file> : specify the configuration file to use.
+
+ -p <platform> : specify platform information.  This is usually
+    something like "Linux-release" or "CYGWIN_NT-5.1-debug".
+
+ -r : reset paths.  This causes the simulator to regenerate the paths
+    based on defaults.  This is useful when copying your .android.cf from
+    a different system, because it updates all the local paths without
+    disturbing the other options.
+
+
+===== Preferences Quick Reference =====
+
+Path preferences.  These are reset by the "-r" flag:
+
+"debugger"            (str) Path to the debugger (usually /usr/bin/gdb).
+"valgrinder"          (str) Path to valgrind (usually /usr/bin/valgrind).
+
+Common prefs:
+
+"auto-power-on"       (bool) Automatically start runtime on simulator start.
+"debug"               (bool) Launch the runtime inside <debugger>.
+"valgrind"            (bool) Launch the runtime inside <valgrinder>.
+"log-*"               (various) Preferences for the log window.
+"window-*"            (int) Positions and sizes of windows.
+"default-device"      (str) Name of the device that opens initially.
+"ld-assume-kernel"    (str) Hack to make goobuntu GDB work; use "" to disable.
+
+Less-common prefs:
+
+"gamma"               (float) Gamma correction factor (default 1.0).
+"window-device-x"     (int) Position of device window.
+"window-device-y"     (int) Position of device window.
+"trap-sigint"         (bool) Catch Ctrl-C.  Kill the runtime when we do.
+"refocus-on-restart"  (bool) When runtime rstarts, give focus to phone window.
+"launch-command"      (str) Command to run, e.g. "xterm -e" (cmd is appended).
+"launch-wrapper-args" (str) Args to launch wrapper, e.g. "-wait -output foo".
+
+
+(If you prefer gnome-terminal to xterm, you can use something like
+"gnome-terminal --disable-factory -x".  The "disable-factory" arg is
+needed to ensure that it inherits the environment variables.)
+
+
+***** NOTE *****
+
+If you're using a non-goobuntu system, make sure "ld-assume-kernel" is ""
+in your .android.cf.  gdb works correctly on Ubuntu 7.04 (fiesty) and 7.10
+(gutsy), and the goobuntu workaround will cause shared library version
+failures on startup.
+
diff --git a/simulator/app/Resource.h b/simulator/app/Resource.h
new file mode 100644
index 0000000..1602428
--- /dev/null
+++ b/simulator/app/Resource.h
@@ -0,0 +1,98 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Resource enumeration.
+//
+#ifndef _SIM_RESOURCE_H
+#define _SIM_RESOURCE_H
+
+/*
+ * IDs for dialogs, controls, menu items, and whatever else comes along.
+ *
+ * Some standard defs are in "wx/defs.h".  They run from 5000 to 6000.
+ */
+enum {
+    // common stuff
+    //ID_ANONYMOUS = -1,        // use wxID_ANY(-1) or wxID_STATIC(5105)
+
+
+    // Menu item IDs
+    IDM_FILE_PREFERENCES = 100,
+    IDM_FILE_EXIT,
+
+    IDM_RUNTIME_START,
+    IDM_RUNTIME_STOP,
+    IDM_RUNTIME_RESTART,
+    IDM_RUNTIME_KILL,
+
+    IDM_DEVICE,
+    IDM_DEVICE_SEL0,
+    // leave space; each phone model gets a menu item ID
+    IDM_DEVICE_SELN = IDM_DEVICE_SEL0 + 32,
+    IDM_DEVICE_RESCAN,
+
+    IDM_DEBUG_SHOW_LOG,
+
+    IDM_HELP_CONTENTS,
+    IDM_HELP_ABOUT,
+
+
+    // Dialog IDs
+    IDD_PREFS,
+    IDD_LOG_PREFS,
+
+    // Control IDs
+    IDC_MODE_SELECT,            // main - combobox
+    IDC_USE_GDB,                // main - checkbox
+    IDC_USE_VALGRIND,           // main - checkbox
+    IDC_CHECK_JNI,              // main - checkbox
+    IDC_JAVA_APP_NAME,          // main - combobox
+    IDC_JAVA_VM,                // main - combobox
+    IDC_OVERLAY_ONION_SKIN,		// main - combobox
+    IDC_ONION_SKIN_FILE_NAME,	// main - textctrl
+    IDC_ONION_SKIN_BUTTON,		// main - button
+    IDC_ONION_SKIN_ALPHA_VAL,	// main - slider
+	
+    IDC_SPREFS_CONFIG_NAME,     // sim prefs page - textctrl
+    IDC_SPREFS_DEBUGGER,        // sim prefs page - textctrl
+    IDC_SPREFS_VALGRINDER,      // sim prefs page - textctrl
+    IDC_SPREFS_AUTO_POWER_ON,   // sim prefs page - checkbox
+
+    IDC_RPREFS_GAMMA,           // runtime prefs page - textctrl
+    IDC_RPREFS_ENABLE_SOUND,    // runtime prefs page - checkbox
+    IDC_RPREFS_ENABLE_FAKE_CAMERA,// runtime prefs page - checkbox
+
+    IDC_LOG_TEXT,               // log window - textctrl
+    IDC_LOG_LEVEL,              // log window - combobox
+    IDC_LOG_CLEAR,              // log window - button
+    IDC_LOG_PAUSE,              // log window - button
+    IDC_LOG_PREFS,              // log window - button
+
+    IDC_LOG_PREFS_FMT_FULL,     // log prefs - radio button
+    IDC_LOG_PREFS_FMT_BRIEF,    // log prefs - radio button
+    IDC_LOG_PREFS_FMT_MINIMAL,  // log prefs - radio button
+    IDC_LOG_PREFS_SINGLE_LINE,  // log prefs - checkbox
+    IDC_LOG_PREFS_EXTRA_SPACING, // log prefs - combobox
+    IDC_LOG_PREFS_POINT_SIZE,   // log prefs - textctrl
+    IDC_LOG_PREFS_USE_COLOR,    // log prefs - checkbox
+    IDC_LOG_PREFS_FONT_MONO,    // log prefs - checkbox
+
+    IDC_LOG_PREFS_DISPLAY_MAX,  // log prefs - textctrl
+    IDC_LOG_PREFS_POOL_SIZE,    // log prefs - textctrl
+
+    IDC_LOG_PREFS_WRITE_FILE,   // log prefs - checkbox
+    IDC_LOG_PREFS_FILENAME,     // log prefs - textctrl
+    IDC_LOG_PREFS_TRUNCATE_OLD, // log prefs - textctrl
+};
+
+/*
+ * Common definitions for control spacing.
+ *
+ * Doesn't really belong here, but it'll do.
+ */
+enum {
+    kEdgeSpacing = 4,       // padding at edge of prefs pages, in pixels
+    kInterSpacing = 5,      // padding between controls, in pixels
+};
+
+#endif // _SIM_RESOURCE_H
diff --git a/simulator/app/Semaphore.cpp b/simulator/app/Semaphore.cpp
new file mode 100644
index 0000000..3b6ee7b
--- /dev/null
+++ b/simulator/app/Semaphore.cpp
@@ -0,0 +1,516 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Inter-process semaphores.
+//
+#include "Semaphore.h"
+
+#if defined(HAVE_MACOSX_IPC)
+# include <semaphore.h>
+#elif defined(HAVE_SYSV_IPC)
+# include <sys/types.h>
+# include <sys/ipc.h>
+# include <sys/sem.h>
+#elif defined(HAVE_WIN32_IPC)
+# include <windows.h>
+#elif defined(HAVE_ANDROID_IPC)
+// not yet
+#else
+# error "unknown sem config"
+#endif
+
+#include <utils/Log.h>
+
+#include <errno.h>
+#include <assert.h>
+
+using namespace android;
+
+
+#if defined(HAVE_ANDROID_IPC) // ----------------------------------------------
+
+Semaphore::Semaphore(void)
+    : mHandle(0), mCreator(false), mKey(-1)
+{}
+
+Semaphore::~Semaphore(void)
+{}
+
+bool Semaphore::create(int key, int initialValue, bool deleteExisting)
+{
+    return false;
+}
+
+bool Semaphore::attach(int key)
+{
+    return false;
+}
+
+void Semaphore::acquire(void)
+{}
+
+void Semaphore::release(void)
+{}
+
+bool Semaphore::tryAcquire(void)
+{
+    return false;
+}
+
+#elif defined(HAVE_MACOSX_IPC) // ---------------------------------------------
+
+/*
+ * The SysV semaphores don't work on all of our machines.  The POSIX
+ * named semaphores seem to work better.
+ */
+
+#define kInvalidHandle SEM_FAILED
+
+static const char* kSemStr = "/tmp/android-sem-";
+
+/*
+ * Constructor.  Just init fields.
+ */
+Semaphore::Semaphore(void)
+    : mHandle((unsigned long) kInvalidHandle), mCreator(false), mKey(-1)
+{
+}
+
+/*
+ * Destructor.  If we created the semaphore, destroy it.
+ */
+Semaphore::~Semaphore(void)
+{
+    LOG(LOG_VERBOSE, "sem", "~Semaphore(handle=%ld creator=%d)\n",
+        mHandle, mCreator);
+
+    if (mHandle != (unsigned long) kInvalidHandle) {
+        sem_close((sem_t*) mHandle);
+
+        if (mCreator) {
+            char nameBuf[64];
+            int cc;
+
+            snprintf(nameBuf, sizeof(nameBuf), "%s%d", kSemStr, mKey);
+
+            cc = sem_unlink(nameBuf);
+            if (cc != 0) {
+                LOG(LOG_ERROR, "sem",
+                    "Failed to remove sem '%s' (errno=%d)\n", nameBuf, errno);
+            }
+        }
+    }
+}
+
+/*
+ * Create the semaphore.
+ */
+bool Semaphore::create(int key, int initialValue, bool deleteExisting)
+{
+    int cc;
+    char nameBuf[64];
+
+    snprintf(nameBuf, sizeof(nameBuf), "%s%d", kSemStr, key);
+
+    if (deleteExisting) {
+        cc = sem_unlink(nameBuf);
+        if (cc != 0 && errno != ENOENT) {
+            LOG(LOG_WARN, "sem", "Warning: failed to remove sem '%s'\n",
+                nameBuf);
+            /* keep going? */
+        }
+    }
+
+    /* create and set initial value */
+    sem_t* semPtr;
+    semPtr = sem_open(nameBuf, O_CREAT | O_EXCL, 0666, 1);
+    if (semPtr == (sem_t*)SEM_FAILED) {
+        LOG(LOG_ERROR, "sem",
+            "ERROR: sem_open failed to create '%s' (errno=%d)\n",
+            nameBuf, errno);
+        return false;
+    }
+
+    mHandle = (unsigned long) semPtr;
+    mCreator = true;
+    mKey = key;
+
+    return true;
+}
+
+/*
+ * Attach to an existing semaphore.
+ */
+bool Semaphore::attach(int key)
+{
+    char nameBuf[64];
+
+    snprintf(nameBuf, sizeof(nameBuf), "%s%d", kSemStr, key);
+
+    sem_t* semPtr;
+    semPtr = sem_open(nameBuf, 0, 0666, 0);
+    if (semPtr == (sem_t*) SEM_FAILED) {
+        LOG(LOG_ERROR, "sem",
+            "ERROR: sem_open failed to attach to '%s' (errno=%d)\n",
+            nameBuf, errno);
+        return false;
+    }
+
+    mHandle = (unsigned long) semPtr;
+    assert(mCreator == false);
+    mKey = key;
+
+    return true;
+}
+
+/*
+ * Acquire or release the semaphore.
+ */
+void Semaphore::acquire(void)
+{
+    int cc = sem_wait((sem_t*) mHandle);
+    if (cc != 0)
+        LOG(LOG_WARN, "sem", "acquire failed (errno=%d)\n", errno);
+}
+void Semaphore::release(void)
+{
+    int cc = sem_post((sem_t*) mHandle);
+    if (cc != 0)
+        LOG(LOG_WARN, "sem", "release failed (errno=%d)\n", errno);
+}
+bool Semaphore::tryAcquire(void)
+{
+    int cc = sem_trywait((sem_t*) mHandle);
+    if (cc != 0) {
+        if (errno != EAGAIN)
+            LOG(LOG_WARN, "sem", "tryAcquire failed (errno=%d)\n", errno);
+        return false;
+    }
+    return true;
+}
+
+
+#elif defined(HAVE_SYSV_IPC) // -----------------------------------------------
+
+/*
+ * Basic SysV semaphore stuff.
+ */
+
+#define kInvalidHandle  ((unsigned long)-1)
+
+#if defined(_SEM_SEMUN_UNDEFINED)
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+    int val;                  /* value for SETVAL */
+    struct semid_ds *buf;     /* buffer for IPC_STAT, IPC_SET */
+    unsigned short *array;    /* array for GETALL, SETALL */
+                              /* Linux specific part: */
+    struct seminfo *__buf;    /* buffer for IPC_INFO */
+};
+#endif
+
+/*
+ * Constructor.  Just init fields.
+ */
+Semaphore::Semaphore(void)
+    : mHandle(kInvalidHandle), mCreator(false)
+{
+}
+
+/*
+ * Destructor.  If we created the semaphore, destroy it.
+ */
+Semaphore::~Semaphore(void)
+{
+    LOG(LOG_VERBOSE, "sem", "~Semaphore(handle=%ld creator=%d)\n",
+        mHandle, mCreator);
+
+    if (mCreator && mHandle != kInvalidHandle) {
+        int cc;
+
+        cc = semctl((int) mHandle, 0, IPC_RMID);
+        if (cc != 0) {
+            LOG(LOG_WARN, "sem",
+                "Destructor failed to destroy key=%ld\n", mHandle);
+        }
+    }
+}
+
+/*
+ * Create the semaphore.
+ */
+bool Semaphore::create(int key, int initialValue, bool deleteExisting)
+{
+    int semid, cc;
+
+    if (deleteExisting) {
+        semid = semget(key, 1, 0);
+        if (semid != -1) {
+            LOG(LOG_DEBUG, "sem", "Key %d exists (semid=%d), removing\n",
+                key, semid);
+            cc = semctl(semid, 0, IPC_RMID);
+            if (cc != 0) {
+                LOG(LOG_ERROR, "sem", "Failed to remove key=%d semid=%d\n",
+                    key, semid);
+                return false;
+            } else {
+                LOG(LOG_DEBUG, "sem",
+                    "Removed previous semaphore with key=%d\n", key);
+            }
+        }
+    }
+
+    semid = semget(key, 1, 0600 | IPC_CREAT | IPC_EXCL);
+    if (semid == -1) {
+        LOG(LOG_ERROR, "sem", "Failed to create key=%d (errno=%d)\n",
+            key, errno);
+        return false;
+    }
+
+    mHandle = semid;
+    mCreator = true;
+    mKey = key;
+
+    /*
+     * Set initial value.
+     */
+    union semun init;
+    init.val = initialValue;
+    cc = semctl(semid, 0, SETVAL, init);
+    if (cc == -1) {
+        LOG(LOG_ERROR, "sem",
+            "Unable to initialize semaphore, key=%d iv=%d (errno=%d)\n",
+            key, initialValue, errno);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Attach to an existing semaphore.
+ */
+bool Semaphore::attach(int key)
+{
+    int semid;
+
+    semid = semget(key, 0, 0);
+    if (semid == -1) {
+        LOG(LOG_ERROR, "sem", "Failed to find key=%d\n", key);
+        return false;
+    }
+
+    mHandle = semid;
+    assert(mCreator == false);
+    mKey = key;
+
+    return true;
+}
+
+/*
+ * Acquire or release the semaphore.
+ */
+void Semaphore::acquire(void)
+{
+    assert(mHandle != kInvalidHandle);
+    adjust(-1, true);
+}
+void Semaphore::release(void)
+{
+    assert(mHandle != kInvalidHandle);
+    adjust(1, true);
+}
+bool Semaphore::tryAcquire(void)
+{
+    assert(mHandle != kInvalidHandle);
+    return adjust(-1, false);
+}
+
+/*
+ * Do the actual semaphore manipulation.
+ *
+ * The semaphore's value indicates the number of free resources.  Pass
+ * in a negative value for "adj" to acquire resources, or a positive
+ * value to free resources.
+ *
+ * Returns true on success, false on failure.
+ */
+bool Semaphore::adjust(int adj, bool wait)
+{
+    struct sembuf op;
+    int cc;
+
+    op.sem_num = 0;
+    op.sem_op = adj;
+    op.sem_flg = SEM_UNDO;
+    if (!wait)
+        op.sem_flg |= IPC_NOWAIT;
+
+    cc = semop((int) mHandle, &op, 1);
+    if (cc != 0) {
+        if (wait || errno != EAGAIN) {
+            LOG(LOG_WARN, "sem",
+                "semaphore adjust by %d failed for semid=%ld (errno=%d)\n",
+                adj, mHandle, errno);
+        }
+        return false;
+    }
+
+    //LOG(LOG_VERBOSE, "sem",
+    //    "adjusted semaphore by %d (semid=%ld)\n", adj, mHandle);
+
+    return true;
+}
+
+
+#elif defined(HAVE_WIN32_IPC) // ----------------------------------------------
+
+/*
+ * Win32 semaphore implementation.
+ *
+ * Pretty straightforward.
+ */
+
+static const char* kSemStr = "android-sem-";
+
+/*
+ * Constructor.  Just init fields.
+ */
+Semaphore::Semaphore(void)
+    : mHandle((unsigned long) INVALID_HANDLE_VALUE), mCreator(false)
+{
+}
+
+/*
+ * Destructor.  Just close the semaphore handle.
+ */
+Semaphore::~Semaphore(void)
+{
+    LOG(LOG_DEBUG, "sem", "~Semaphore(handle=%ld creator=%d)\n",
+        mHandle, mCreator);
+
+    if (mHandle != (unsigned long) INVALID_HANDLE_VALUE)
+        CloseHandle((HANDLE) mHandle);
+}
+
+/*
+ * Create the semaphore.
+ */
+bool Semaphore::create(int key, int initialValue, bool deleteExisting)
+{
+    char keyBuf[64];
+    HANDLE hSem;
+    long max;
+
+    snprintf(keyBuf, sizeof(keyBuf), "%s%d", kSemStr, key);
+
+    if (initialValue == 0)
+        max = 1;
+    else
+        max = initialValue;
+
+    hSem = CreateSemaphore(
+            NULL,                       // security attributes
+            initialValue,               // initial count
+            max,                        // max count, must be >= initial
+            keyBuf);                    // object name
+    if (hSem == NULL) {
+        DWORD err = GetLastError();
+        if (err == ERROR_ALREADY_EXISTS) {
+            LOG(LOG_ERROR, "sem", "Semaphore '%s' already exists\n", keyBuf);
+        } else {
+            LOG(LOG_ERROR, "sem", "CreateSemaphore(%s) failed (err=%ld)\n",
+                keyBuf, err);
+        }
+        return false;
+    }
+
+    mHandle = (unsigned long) hSem;
+    mCreator = true;
+    mKey = key;
+
+    //LOG(LOG_DEBUG, "sem", "Semaphore '%s' created (handle=0x%08lx)\n",
+    //    keyBuf, mHandle);
+
+    return true;
+}
+
+/*
+ * Attach to an existing semaphore.
+ */
+bool Semaphore::attach(int key)
+{
+    char keyBuf[64];
+    HANDLE hSem;
+
+    snprintf(keyBuf, sizeof(keyBuf), "%s%d", kSemStr, key);
+
+    hSem = OpenSemaphore(
+            //SEMAPHORE_MODIFY_STATE,   // mostly-full access
+            SEMAPHORE_ALL_ACCESS,       // full access
+            FALSE,                      // don't let kids inherit handle
+            keyBuf);                    // object name
+    if (hSem == NULL) {
+        LOG(LOG_ERROR, "sem", "OpenSemaphore(%s) failed (err=%ld)\n",
+            keyBuf, GetLastError());
+        return false;
+    }
+
+    mHandle = (unsigned long) hSem;
+    assert(mCreator == false);
+    mKey = key;
+
+    return true;
+}
+
+/*
+ * Acquire or release the semaphore.
+ */
+void Semaphore::acquire(void)
+{
+    DWORD result;
+
+    assert(mHandle != (unsigned long) INVALID_HANDLE_VALUE);
+
+    result = WaitForSingleObject((HANDLE) mHandle, INFINITE);
+    if (result != WAIT_OBJECT_0) {
+        LOG(LOG_WARN, "sem",
+            "WaitForSingleObject(INF) on semaphore returned %ld (err=%ld)\n",
+            result, GetLastError());
+    }
+}
+void Semaphore::release(void)
+{
+    DWORD result;
+
+    assert(mHandle != (unsigned long) INVALID_HANDLE_VALUE);
+
+    result = ReleaseSemaphore((HANDLE) mHandle, 1, NULL);    // incr by 1
+    if (result == 0) {
+        LOG(LOG_WARN, "sem", "ReleaseSemaphore failed (err=%ld)\n",
+            GetLastError());
+    }
+}
+bool Semaphore::tryAcquire(void)
+{
+    DWORD result;
+
+    assert(mHandle != (unsigned long) INVALID_HANDLE_VALUE);
+    result = WaitForSingleObject((HANDLE) mHandle, 0);
+    if (result == WAIT_OBJECT_0)
+        return true;        // grabbed it
+    else if (result == WAIT_TIMEOUT)
+        return false;       // not available
+    else if (result == WAIT_FAILED) {
+        LOG(LOG_WARN, "sem", "WaitForSingleObject(0) on sem failed (err=%ld)\n",
+            GetLastError());
+        return false;
+    } else {
+        LOG(LOG_WARN, "sem",
+            "WaitForSingleObject(0) on sem returned %ld (err=%ld)\n",
+            result, GetLastError());
+        return false;
+    }
+}
+
+#endif // ---------------------------------------------------------------------
diff --git a/simulator/app/Semaphore.h b/simulator/app/Semaphore.h
new file mode 100644
index 0000000..3b834f3
--- /dev/null
+++ b/simulator/app/Semaphore.h
@@ -0,0 +1,59 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Inter-process semaphore.
+//
+// These are meant for IPC, not thread management.  The Mutex and Condition
+// classes are much lighter weight.
+//
+#ifndef __LIBS_SEMAPHORE_H
+#define __LIBS_SEMAPHORE_H
+
+#ifdef HAVE_ANDROID_OS
+#error DO NOT USE THIS FILE IN THE DEVICE BUILD
+#endif
+
+namespace android {
+
+/*
+ * Platform-independent semaphore class.
+ *
+ * Each object holds a single semaphore.
+ *
+ * The "key" is usually the process ID of the process that created the
+ * semaphore (following POSIX semantics).  See the comments in shmem.h.
+ */
+class Semaphore {
+public:
+    Semaphore(void);
+    virtual ~Semaphore(void);
+
+    /*
+     * Create a new semaphore, with the specified initial value.  The
+     * value indicates the number of resources available.
+     */
+    bool create(int key, int initialValue, bool deleteExisting);
+
+    /*
+     * Attach to an existing semaphore.
+     */
+    bool attach(int key);
+
+    /*
+     * Acquire or release the semaphore.
+     */
+    void acquire(void);
+    void release(void);
+    bool tryAcquire(void);      // take a timeout?
+
+private:
+    bool adjust(int adj, bool wait);
+
+    unsigned long   mHandle;    // semid(int) or HANDLE
+    bool            mCreator;
+    int             mKey;
+};
+
+}; // namespace android
+
+#endif // __LIBS_SEMAPHORE_H
diff --git a/simulator/app/Shmem.cpp b/simulator/app/Shmem.cpp
new file mode 100644
index 0000000..4c619c2
--- /dev/null
+++ b/simulator/app/Shmem.cpp
@@ -0,0 +1,558 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Shared memory interface.
+//
+#include "Shmem.h"
+#include "utils/Log.h"
+
+#if defined(HAVE_MACOSX_IPC) || defined(HAVE_ANDROID_IPC)
+#  include <sys/mman.h>
+#  include <fcntl.h>
+#  include <unistd.h>
+#elif defined(HAVE_SYSV_IPC)
+# include <sys/types.h>
+# include <sys/ipc.h>
+# include <sys/shm.h>
+#elif defined(HAVE_WIN32_IPC)
+# include <windows.h>
+#else
+# error "unknown shm config"
+#endif
+
+#include <errno.h>
+#include <assert.h>
+
+using namespace android;
+
+
+#if defined(HAVE_MACOSX_IPC) || defined(HAVE_ANDROID_IPC)
+
+/*
+ * SysV IPC under Mac OS X seems to have problems.  It works fine on
+ * some machines but totally fails on others.  We're working around it
+ * here by using mmap().
+ */
+
+#define kInvalidHandle  ((unsigned long)-1)
+
+static const char* kShmemFile = "/tmp/android-";
+
+/*
+ * Constructor.  Just set up the fields.
+ */
+Shmem::Shmem(void)
+    : mHandle(kInvalidHandle), mAddr(MAP_FAILED), mLength(-1), mCreator(false),
+      mKey(-1)
+{
+}
+
+/*
+ * Destructor.  Detach and, if we created it, mark the segment for
+ * destruction.
+ */
+Shmem::~Shmem(void)
+{
+    if (mAddr != MAP_FAILED)
+        munmap(mAddr, mLength);
+    if ((long)mHandle >= 0) {
+        close(mHandle);
+
+        if (mCreator) {
+            char nameBuf[64];
+            int cc;
+
+            snprintf(nameBuf, sizeof(nameBuf), "%s%d", kShmemFile, mKey);
+            cc = unlink(nameBuf);
+            if (cc != 0) {
+                LOG(LOG_WARN, "shmem", "Couldn't clean up '%s'\n", nameBuf);
+                /* oh well */
+            }
+        }
+    }
+}
+
+/*
+ * Create the segment and attach ourselves to it.
+ */
+bool Shmem::create(int key, long size, bool deleteExisting)
+{
+    char nameBuf[64];
+    int fd, cc;
+
+    snprintf(nameBuf, sizeof(nameBuf), "%s%d", kShmemFile, key);
+
+    if (deleteExisting) {
+        cc = unlink(nameBuf);
+        if (cc != 0 && errno != ENOENT) {
+            LOG(LOG_ERROR, "shmem", "Failed to remove old map file '%s'\n",
+                nameBuf);
+            return false;
+        }
+    }
+
+    fd = open(nameBuf, O_CREAT|O_EXCL|O_RDWR, 0600);
+    if (fd < 0) {
+        LOG(LOG_ERROR, "shmem", "Unable to create map file '%s' (errno=%d)\n",
+            nameBuf, errno);
+        return false;
+    }
+
+    /*
+     * Set the file size by seeking and writing.
+     */
+    if (ftruncate(fd, size) == -1) {
+        LOG(LOG_ERROR, "shmem", "Unable to set file size in '%s' (errno=%d)\n",
+            nameBuf, errno);
+        close(fd);
+        return false;
+    }
+
+    mAddr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+    if (mAddr == MAP_FAILED) {
+        LOG(LOG_ERROR, "shmem", "mmap failed (errno=%d)\n", errno);
+        close(fd);
+        return false;
+    }
+
+    mHandle = fd;
+    mLength = size;
+    mCreator = true;
+    mKey = key;
+
+    /* done with shmem, create the associated semaphore */
+    if (!mSem.create(key, 1, true)) {
+        LOG(LOG_ERROR, "shmem",
+            "Failed creating semaphore for Shmem (key=%d)\n", key);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Attach ourselves to an existing segment.
+ */
+bool Shmem::attach(int key)
+{
+    char nameBuf[64];
+    int fd;
+
+    snprintf(nameBuf, sizeof(nameBuf), "%s%d", kShmemFile, key);
+    fd = open(nameBuf, O_RDWR, 0600);
+    if (fd < 0) {
+        LOG(LOG_ERROR, "shmem", "Unable to open map file '%s' (errno=%d)\n",
+            nameBuf, errno);
+        return false;
+    }
+
+    off_t len;
+    len = lseek(fd, 0, SEEK_END);
+    if (len == (off_t) -1) {
+        LOG(LOG_ERROR, "shmem",
+            "Could not determine file size of '%s' (errno=%d)\n",
+            nameBuf, errno);
+        close(fd);
+        return false;
+    }
+
+    mAddr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+    if (mAddr == MAP_FAILED) {
+        LOG(LOG_ERROR, "shmem", "mmap failed (errno=%d)\n", errno);
+        close(fd);
+        return false;
+    }
+
+    mHandle = fd;
+    mLength = len;
+    assert(mCreator == false);
+    mKey = key;
+
+    /* done with shmem, attach to associated semaphore */
+    if (!mSem.attach(key)) {
+        LOG(LOG_ERROR, "shmem",
+            "Failed to attach to semaphore for Shmem (key=%d)\n", key);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Get address.
+ */
+void* Shmem::getAddr(void)
+{
+    assert(mAddr != MAP_FAILED);
+    return mAddr;
+}
+
+/*
+ * Return the length of the segment.
+ *
+ * Returns -1 on failure.
+ */
+long Shmem::getLength(void)
+{
+    if (mLength >= 0)
+        return mLength;
+
+    // we should always have it by now
+    assert(false);
+    return -1;
+}
+
+
+#elif defined(HAVE_SYSV_IPC) // ----------------------------------------------
+
+/*
+ * SysV-style IPC.  The SysV shared memory API is fairly annoying to
+ * deal with, but it's present on many UNIX-like systems.
+ */
+
+#define kInvalidHandle  ((unsigned long)-1)
+
+/*
+ * Constructor.  Just set up the fields.
+ */
+Shmem::Shmem(void)
+    : mHandle(kInvalidHandle), mAddr(NULL), mLength(-1), mCreator(false),
+      mKey(-1)
+{
+}
+
+/*
+ * Destructor.  Detach and, if we created it, mark the segment for
+ * destruction.
+ */
+Shmem::~Shmem(void)
+{
+    int cc;
+
+    //LOG(LOG_DEBUG, "shmem", "~Shmem(handle=%ld creator=%d)",
+    //    mHandle, mCreator);
+
+    if (mAddr != NULL)
+        cc = shmdt(mAddr);
+
+    if (mCreator && mHandle != kInvalidHandle) {
+        cc = shmctl((int) mHandle, IPC_RMID, NULL);
+        if (cc != 0) {
+            LOG(LOG_WARN, "shmem",
+                "Destructor failed to remove shmid=%ld (errno=%d)\n",
+                mHandle, errno);
+        }
+    }
+}
+
+/*
+ * Create the segment and attach ourselves to it.
+ */
+bool Shmem::create(int key, long size, bool deleteExisting)
+{
+    int shmid, cc;
+
+    if (deleteExisting) {
+        shmid = shmget(key, size, 0);
+        if (shmid != -1) {
+            LOG(LOG_DEBUG, "shmem",
+                "Key %d exists (shmid=%d), marking for destroy", key, shmid);
+            cc = shmctl(shmid, IPC_RMID, NULL);
+            if (cc != 0) {
+                LOG(LOG_ERROR, "shmem",
+                    "Failed to remove key=%d shmid=%d (errno=%d)\n",
+                    key, shmid, errno);
+                return false;   // IPC_CREAT | IPC_EXCL will fail, so bail now
+            } else {
+                LOG(LOG_DEBUG, "shmem",
+                    "Removed previous segment with key=%d\n", key);
+            }
+        }
+    }
+
+    shmid = shmget(key, size, 0600 | IPC_CREAT | IPC_EXCL);
+    if (shmid == -1) {
+        LOG(LOG_ERROR, "shmem", "Failed to create key=%d (errno=%d)\n",
+            key, errno);
+        return false;
+    }
+
+    mHandle = shmid;
+    mCreator = true;
+    mKey = key;
+
+    void* addr = shmat(shmid, NULL, 0);
+    if (addr == (void*) -1) {
+        LOG(LOG_ERROR, "shmem",
+            "Could not attach to key=%d shmid=%d (errno=%d)\n",
+            key, shmid, errno);
+        return false;
+    }
+
+    mAddr = addr;
+    mLength = size;
+
+    /* done with shmem, create the associated semaphore */
+    if (!mSem.create(key, 1, true)) {
+        LOG(LOG_ERROR, "shmem",
+            "Failed creating semaphore for Shmem (key=%d)\n", key);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Attach ourselves to an existing segment.
+ */
+bool Shmem::attach(int key)
+{
+    int shmid;
+
+    shmid = shmget(key, 0, 0);
+    if (shmid == -1) {
+        LOG(LOG_ERROR, "shmem", "Failed to find key=%d\n", key);
+        return false;
+    }
+
+    mHandle = shmid;
+    assert(mCreator == false);
+    mKey = key;
+
+    void* addr = shmat(shmid, NULL, 0);
+    if (addr == (void*) -1) {
+        LOG(LOG_ERROR, "shmem", "Could not attach to key=%d shmid=%d\n",
+            key, shmid);
+        return false;
+    }
+
+    mAddr = addr;
+
+    /* done with shmem, attach to associated semaphore */
+    if (!mSem.attach(key)) {
+        LOG(LOG_ERROR, "shmem",
+            "Failed to attach to semaphore for Shmem (key=%d)\n", key);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Get address.
+ */
+void* Shmem::getAddr(void)
+{
+    assert(mAddr != NULL);
+    return mAddr;
+}
+
+/*
+ * Return the length of the segment.
+ *
+ * Returns -1 on failure.
+ */
+long Shmem::getLength(void)
+{
+    if (mLength >= 0)
+        return mLength;
+
+    assert(mHandle != kInvalidHandle);
+
+    struct shmid_ds shmids;
+    int cc;
+
+    cc = shmctl((int) mHandle, IPC_STAT, &shmids);
+    if (cc != 0) {
+        LOG(LOG_ERROR, "shmem", "Could not IPC_STAT shmid=%ld\n", mHandle);
+        return -1;
+    }
+    mLength = shmids.shm_segsz;     // save a copy to avoid future lookups
+
+    return mLength;
+}
+
+
+#elif defined(HAVE_WIN32_IPC) // ---------------------------------------------
+
+/*
+ * Win32 shared memory implementation.
+ *
+ * Shared memory is implemented as an "anonymous" file mapping, using the
+ * memory-mapped I/O interfaces.
+ */
+
+static const char* kShmemStr = "android-shmem-";
+
+/*
+ * Constructor.  Just set up the fields.
+ */
+Shmem::Shmem(void)
+    : mHandle((unsigned long) INVALID_HANDLE_VALUE),
+      mAddr(NULL), mLength(-1), mCreator(false), mKey(-1)
+{
+}
+
+/*
+ * Destructor.  The Win32 API doesn't require a distinction between
+ * the "creator" and other mappers.
+ */
+Shmem::~Shmem(void)
+{
+    LOG(LOG_DEBUG, "shmem", "~Shmem(handle=%ld creator=%d)",
+        mHandle, mCreator);
+
+    if (mAddr != NULL)
+        UnmapViewOfFile(mAddr);
+    if (mHandle != (unsigned long) INVALID_HANDLE_VALUE)
+        CloseHandle((HANDLE) mHandle);
+}
+
+/*
+ * Create the segment and map it.
+ */
+bool Shmem::create(int key, long size, bool deleteExisting)
+{
+    char keyBuf[64];
+    HANDLE hMapFile;
+
+    snprintf(keyBuf, sizeof(keyBuf), "%s%d", kShmemStr, key);
+
+    hMapFile = CreateFileMapping(
+                INVALID_HANDLE_VALUE,       // use paging file, not actual file
+                NULL,                       // default security
+                PAGE_READWRITE,             // read/write access
+                0,                          // max size; no need to cap
+                size,                       // min size
+                keyBuf);                    // mapping name
+    if (hMapFile == NULL || hMapFile == INVALID_HANDLE_VALUE) {
+        LOG(LOG_ERROR, "shmem",
+            "Could not create mapping object '%s' (err=%ld)\n",
+            keyBuf, GetLastError());
+        return false;
+    }
+
+    mHandle = (unsigned long) hMapFile;
+    mCreator = true;
+    mKey = key;
+
+    mAddr = MapViewOfFile(
+                hMapFile,                   // handle to map object
+                FILE_MAP_ALL_ACCESS,        // read/write
+                0,                          // offset (hi)
+                0,                          // offset (lo)
+                size);                      // #of bytes to map
+    if (mAddr == NULL) {
+        LOG(LOG_ERROR, "shmem", "Could not map shared area (err=%ld)\n",
+            GetLastError());
+        return false;
+    }
+    mLength = size;
+
+    /* done with shmem, create the associated semaphore */
+    if (!mSem.create(key, 1, true)) {
+        LOG(LOG_ERROR, "shmem",
+            "Failed creating semaphore for Shmem (key=%d)\n", key);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Attach ourselves to an existing segment.
+ */
+bool Shmem::attach(int key)
+{
+    char keyBuf[64];
+    HANDLE hMapFile;
+
+    snprintf(keyBuf, sizeof(keyBuf), "%s%d", kShmemStr, key);
+
+    hMapFile = OpenFileMapping(
+                FILE_MAP_ALL_ACCESS,        // read/write
+                FALSE,                      // don't let kids inherit handle
+                keyBuf);                    // mapping name
+    if (hMapFile == NULL) {
+        LOG(LOG_ERROR, "shmem",
+            "Could not open mapping object '%s' (err=%ld)\n",
+            keyBuf, GetLastError());
+        return false;
+    }
+
+    mHandle = (unsigned long) hMapFile;
+    assert(mCreator == false);
+    mKey = key;
+
+    mAddr = MapViewOfFile(
+                hMapFile,                   // handle to map object
+                FILE_MAP_ALL_ACCESS,        // read/write
+                0,                          // offset (hi)
+                0,                          // offset (lo)
+                0);                        // #of bytes to map
+    if (mAddr == NULL) {
+        LOG(LOG_ERROR, "shmem", "Could not map shared area (err=%ld)\n",
+            GetLastError());
+        return false;
+    }
+
+    /* done with shmem, attach to associated semaphore */
+    if (!mSem.attach(key)) {
+        LOG(LOG_ERROR, "shmem",
+            "Failed to attach to semaphore for Shmem (key=%d)\n", key);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Get address.
+ */
+void* Shmem::getAddr(void)
+{
+    assert(mAddr != NULL);
+    return mAddr;
+}
+
+/*
+ * Get the length of the segment.
+ */
+long Shmem::getLength(void)
+{
+    SIZE_T size;
+    MEMORY_BASIC_INFORMATION mbInfo;
+
+    if (mLength >= 0)
+        return mLength;
+
+    assert(mAddr != NULL);
+
+    size = VirtualQuery(mAddr, &mbInfo, sizeof(mbInfo));
+    if (size == 0) {
+        LOG(LOG_WARN, "shmem", "VirtualQuery returned no data\n");
+        return -1;
+    }
+
+    mLength = mbInfo.RegionSize;
+    return mLength;
+}
+
+#endif // --------------------------------------------------------------------
+
+/*
+ * Semaphore operations.
+ */
+void Shmem::lock(void)
+{
+    mSem.acquire();
+}
+void Shmem::unlock(void)
+{
+    mSem.release();
+}
+bool Shmem::tryLock(void)
+{
+    return mSem.tryAcquire();
+}
+
diff --git a/simulator/app/Shmem.h b/simulator/app/Shmem.h
new file mode 100644
index 0000000..3d53b58
--- /dev/null
+++ b/simulator/app/Shmem.h
@@ -0,0 +1,95 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Inter-process shared memory.
+//
+#ifndef __LIBS_SHMEM_H
+#define __LIBS_SHMEM_H
+
+#ifdef HAVE_ANDROID_OS
+#error DO NOT USE THIS FILE IN THE DEVICE BUILD
+#endif
+
+#include "Semaphore.h"
+
+namespace android {
+
+/*
+ * Platform-independent shared memory.  Each object can be used to
+ * create a chunk of memory that is shared between processes.
+ *
+ * For convenience, a semaphore is associated with each segment.
+ * (Whether this should have been done in a subclass is debatable.)
+ *
+ * The "key" is usually the process ID of the process that created the
+ * segment.  The goal is to avoid clashing with other processes that are
+ * trying to do the same thing.  It's a little awkward to use when you
+ * want to have multiple shared segments created in one process.  In
+ * SysV you can work around this by using a "private" key and sharing
+ * the shmid with your friends, in Win32 you can use a string, but we're
+ * in lowest-common-denominator mode here.  Assuming we have 16-bit PIDs,
+ * the upper 16 bits can be used to serialize keys.
+ *
+ * When the object goes out of scope, the shared memory segment is
+ * detached from the process.  If the object was responsible for creating
+ * the segment, it is also marked for destruction on SysV systems.  This
+ * will make it impossible for others to attach to.
+ *
+ * On some systems, the length returned by getLength() may be different
+ * for parent and child due to page size rounding.
+ */
+class Shmem {
+public:
+    Shmem(void);
+    virtual ~Shmem(void);
+
+    /*
+     * Create a new shared memory segment, with the specified size.  If
+     * "deleteExisting" is set, any existing segment will be deleted first
+     * (useful for SysV IPC).
+     *
+     * Returns "true" on success, "false" on failure.
+     */
+    bool create(int key, long size, bool deleteExisting);
+
+    /*
+     * Attach to a shared memory segment.  Use this from the process that
+     * didn't create the segment.
+     *
+     * Returns "true" on success, "false" on failure.
+     */
+    bool attach(int key);
+
+    /*
+     * Get the memory segment address and length.  These will not change
+     * for the lifetime of the object, so it's okay to cache the results.
+     *
+     * On failure, getAddr() returns NULL and getLength() returns -1.
+     */
+    void* getAddr(void);
+    long getLength(void);
+
+    /*
+     * Lock or unlock the shared memory segment.  This is useful if you
+     * are updating pieces of shared data.  The segment is initially
+     * "unlocked".
+     *
+     * This does *not* lock down the segment in the virtual paging system.
+     * It's just a mutex.
+     */
+    void lock(void);
+    void unlock(void);
+    bool tryLock(void);
+
+private:
+    Semaphore       mSem;       // uses the same value for "key"
+    unsigned long   mHandle;    // shmid(int) or HANDLE
+    void*           mAddr;      // address
+    long            mLength;    // length of segment (cached)
+    bool            mCreator;   // true if we created the segment
+    int             mKey;       // key passed in as arg
+};
+
+}; // namespace android
+
+#endif // __LIBS_SHMEM_H
diff --git a/simulator/app/SimRuntime.h b/simulator/app/SimRuntime.h
new file mode 100644
index 0000000..6dadfb2
--- /dev/null
+++ b/simulator/app/SimRuntime.h
@@ -0,0 +1,124 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Miscellaneous definitions and declarations used for interaction
+// between the device and the simulator.
+//
+// This header is included on both sides, so try not to include
+// any other headers from here.
+//
+#ifndef _RUNTIME_SIMULATOR_H
+#define _RUNTIME_SIMULATOR_H
+
+#include "MessageStream.h"
+#include "Shmem.h"
+//#include "utils/RefBase.h"
+#include "utils/Log.h"
+
+namespace android {
+
+#define ANDROID_PIPE_NAME "runtime"
+
+/*
+ * Hold simulator state.
+ */
+class Simulator {
+public:
+    Simulator(void);
+    ~Simulator(void);
+
+    /*
+     * Commands exchanged between simulator and runtime.
+     */
+    typedef enum Command {
+        kCommandUnknown = 0,
+
+        /* sent from sim to runtime */
+        kCommandGoAway,             // sim says: go away, I'm busy
+        kCommandConfigDone,         // sim says: done sending config
+        kCommandQuit,               // quit nicely
+        kCommandNewPGroup,          // process group management
+        kCommandKeyDown,            // key has been pressed
+        kCommandKeyUp,              // key has been released
+        kCommandTouch,              // finger touched/lifted/dragged
+
+        /* sent from runtime to sim */
+        kCommandNewPGroupCreated,    // send process group as argument
+        kCommandRuntimeReady,       // we're initialized and about to start
+        kCommandUpdateDisplay,      // display has been updated
+        kCommandVibrate,            // vibrate on or off
+    } Command;
+
+    typedef enum TouchMode {
+        kTouchDown = 0,
+        kTouchUp = 1,
+        kTouchDrag = 2
+    } TouchMode;
+
+    /*
+     * Some parameters for config exchange.
+     */
+    enum {
+        kDisplayConfigMagic = 0x44495350,
+        kValuesPerDisplay = 5,
+    };
+
+    /*
+     * Set up communication with parent process.
+     */
+    //bool create(ParentProcess* pParent);
+
+    /*
+     * Set up communication with detached simulator.
+     */
+    bool create(Pipe* reader, Pipe* writer);
+
+    /*
+     * Tell simulator that we're ready to go.
+     */
+    void sendRuntimeReady(void);
+
+    /*
+     * Tell the simulator that a display has been refreshed.
+     */
+    void sendDisplayUpdate(int displayIndex);
+
+    /*
+     * Tell the simulator to turn the vibrator on or off
+     */
+    void sendVibrate(int vibrateOn);
+
+    /*
+     * Get a pointer to the shared memory for the Nth display.
+     */
+    Shmem* getGraphicsBuffer(int displayIndex);
+
+    /*
+     * Return a copy of our input pipe so the event system can monitor
+     * it for pending activity.
+     */
+    Pipe* getReadPipe(void) { return mStream.getReadPipe(); }
+
+    /*
+     * Retrieve the next command from the parent.  Returns NO_ERROR
+     * if all is okay, WOULD_BLOCK if blocking is false and there
+     * are no pending commands, or INVALID_OPERATION if the simulator
+     * has disappeared.
+     */
+    int getNextKey(int32_t* outKey, bool* outDown);
+
+    /*
+     * Log system callback function.
+     */
+    static void writeLogMsg(const android_LogBundle* pBundle);
+
+private:
+    bool finishCreate(void);
+    bool handleDisplayConfig(const long* pData, int length);
+
+    MessageStream   mStream;
+};
+
+}; // namespace android
+
+#endif // _RUNTIME_SIMULATOR_H
diff --git a/simulator/app/UserEvent.cpp b/simulator/app/UserEvent.cpp
new file mode 100644
index 0000000..01d3337
--- /dev/null
+++ b/simulator/app/UserEvent.cpp
@@ -0,0 +1,20 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Some additional glue for "user event" type.
+//
+
+// 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 "UserEvent.h"
+
+DEFINE_EVENT_TYPE(wxEVT_USER_EVENT)
+
+IMPLEMENT_DYNAMIC_CLASS(UserEvent, wxEvent)
+
diff --git a/simulator/app/UserEvent.h b/simulator/app/UserEvent.h
new file mode 100644
index 0000000..76664e1
--- /dev/null
+++ b/simulator/app/UserEvent.h
@@ -0,0 +1,54 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// A "user event" for wxWidgets.
+//
+#ifndef _SIM_USER_EVENT_H
+#define _SIM_USER_EVENT_H
+
+/*
+ * Event declaration.  The book says to use DECLARE_EVENT_TYPE, but that
+ * causes a compiler warning and a link failure with gcc under MinGW.
+ *
+ * It looks like the "magic number", in this case 12345, is just picked
+ * by hand.  There may be a better mechanism in this version of
+ * wxWidgets, but the documentation and sample code doesn't reflect it.
+ */
+BEGIN_DECLARE_EVENT_TYPES()
+    DECLARE_LOCAL_EVENT_TYPE(wxEVT_USER_EVENT, 12345)
+END_DECLARE_EVENT_TYPES()
+
+/*
+ * A "user event" class.  This can be used like any other wxWidgets
+ * event, but we get to stuff anything we want to in it.
+ */
+class UserEvent : public wxEvent {
+public:
+    UserEvent(int id = 0, void* data = (void*) 0)
+        : wxEvent(id, wxEVT_USER_EVENT), mData(data)
+        {}
+    UserEvent(const UserEvent& event)
+        : wxEvent(event), mData(event.mData)
+        {}
+
+    virtual wxEvent* Clone() const {
+        return new UserEvent(*this);
+    }
+
+    void* GetData(void) const { return mData; }
+
+    DECLARE_DYNAMIC_CLASS(UserEvent);
+
+private:
+    UserEvent& operator=(const UserEvent&);     // not implemented
+    void*   mData;
+};
+
+typedef void (wxEvtHandler::*UserEventFunction)(UserEvent&);
+
+#define EVT_USER_EVENT(fn) \
+        DECLARE_EVENT_TABLE_ENTRY(wxEVT_USER_EVENT, wxID_ANY, wxID_ANY, \
+            (wxObjectEventFunction)(wxEventFunction)(UserEventFunction)&fn, \
+            (wxObject*) NULL ),
+
+#endif // _SIM_USER_EVENT_H
diff --git a/simulator/app/UserEventMessage.h b/simulator/app/UserEventMessage.h
new file mode 100644
index 0000000..4a66cc2
--- /dev/null
+++ b/simulator/app/UserEventMessage.h
@@ -0,0 +1,84 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Contents of the "user event" sent from the device thread.
+//
+#ifndef _SIM_USER_EVENT_MESSAGE_H
+#define _SIM_USER_EVENT_MESSAGE_H
+
+#include <utils.h>
+#include "LogMessage.h"
+
+/*
+ * This gets stuffed into a UserEvent, which is posted to the main thread
+ * from a worker thread.
+ *
+ * The object does NOT own anything you stuff into it.  It's just a vehicle
+ * for carting data from one thread to another in a wxWidgets-safe manner,
+ * usually as pointers to data that can be shared between threads.
+ */
+class UserEventMessage {
+public:
+    /*
+     * What type of message is this?
+     */
+    typedef enum UEMType {
+        kUnknown = 0,
+
+        kRuntimeStarted,
+        kRuntimeStopped,
+        kErrorMessage,      // message in mString
+        kLogMessage,        // ptr to heap-allocated LogMessage
+        kExternalRuntime,   // external runtime wants to party
+    } UEMType;
+
+    UserEventMessage(void)
+        : mType(kUnknown), mpLogMessage(NULL)
+        {}
+    ~UserEventMessage(void) {
+    }
+
+    /*
+     * Create one of our various messages.
+     */
+    void CreateRuntimeStarted(void) {
+        mType = kRuntimeStarted;
+    }
+    void CreateRuntimeStopped(void) {
+        mType = kRuntimeStopped;
+    }
+    void CreateErrorMessage(wxString& str) {
+        mType = kErrorMessage;
+        mString = str;
+    }
+    void CreateLogMessage(LogMessage* pLogMessage) {
+        mType = kLogMessage;
+        mpLogMessage = pLogMessage;
+    }
+    void CreateExternalRuntime(android::Pipe* reader, android::Pipe* writer) {
+        mType = kExternalRuntime;
+        mReader = reader;
+        mWriter = writer;
+    }
+
+    /*
+     * Accessors.
+     */
+    UEMType GetType(void) const { return mType; }
+    const wxString& GetString(void) const { return mString; }
+    LogMessage* GetLogMessage(void) const { return mpLogMessage; }
+    android::Pipe* GetReader(void) const { return mReader; }
+    android::Pipe* GetWriter(void) const { return mWriter; }
+
+private:
+    UserEventMessage& operator=(const UserEventMessage&);   // not implemented
+    UserEventMessage(const UserEventMessage&);              // not implemented
+
+    UEMType     mType;
+    wxString    mString;            // for kErrorMessage
+    LogMessage* mpLogMessage;       // for kLogMessage
+    android::Pipe*  mReader;        // for kExternalRuntime
+    android::Pipe*  mWriter;        // for kExternalRuntime
+};
+
+#endif // _SIM_USER_EVENT_MESSAGE_H
diff --git a/simulator/app/assets/android-dream/arrow_down.png b/simulator/app/assets/android-dream/arrow_down.png
new file mode 100644
index 0000000..19b3764
--- /dev/null
+++ b/simulator/app/assets/android-dream/arrow_down.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/arrow_left.png b/simulator/app/assets/android-dream/arrow_left.png
new file mode 100644
index 0000000..113e584
--- /dev/null
+++ b/simulator/app/assets/android-dream/arrow_left.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/arrow_right.png b/simulator/app/assets/android-dream/arrow_right.png
new file mode 100644
index 0000000..ffe3356
--- /dev/null
+++ b/simulator/app/assets/android-dream/arrow_right.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/arrow_up.png b/simulator/app/assets/android-dream/arrow_up.png
new file mode 100644
index 0000000..81c54df
--- /dev/null
+++ b/simulator/app/assets/android-dream/arrow_up.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/back.png b/simulator/app/assets/android-dream/back.png
new file mode 100644
index 0000000..41034d9
--- /dev/null
+++ b/simulator/app/assets/android-dream/back.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/background.png b/simulator/app/assets/android-dream/background.png
new file mode 100644
index 0000000..bab8c6d
--- /dev/null
+++ b/simulator/app/assets/android-dream/background.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/end.png b/simulator/app/assets/android-dream/end.png
new file mode 100644
index 0000000..6830a60
--- /dev/null
+++ b/simulator/app/assets/android-dream/end.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/home.png b/simulator/app/assets/android-dream/home.png
new file mode 100644
index 0000000..7d02136
--- /dev/null
+++ b/simulator/app/assets/android-dream/home.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/key.png b/simulator/app/assets/android-dream/key.png
new file mode 100644
index 0000000..7a3f563
--- /dev/null
+++ b/simulator/app/assets/android-dream/key.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/layout.xml b/simulator/app/assets/android-dream/layout.xml
new file mode 100644
index 0000000..8a7c5f8
--- /dev/null
+++ b/simulator/app/assets/android-dream/layout.xml
@@ -0,0 +1,356 @@
+<?xml version="1.0" ?>
+<!-- Copyright 2006 The Android Open Source Project -->
+
+<device name="Dream">
+    <!-- title for menus -->
+    <title>Android Dream</title>
+
+    <display name="main" width="320" height="480" format="rgb565" refresh="30"/>
+
+    <keyboard qwerty="1" keycharmap="qwerty2" />
+    
+    <mode name="closed">
+        <view display="main" x="75" y="84" rotate="0">
+
+            <!-- surrounding device image and "patches", drawn in order -->
+            <image src="background.png" x="0" y="0"/>
+
+            <!-- buttons for which we have highlight images -->
+            <button keyCode="1"             src="key.png"           x="468" y="276"/>
+            <button keyCode="q"             src="key.png"           x="468" y="312"/>
+            <button keyCode="power"         src="power.png"         x="7"   y="66"/>
+            <button keyCode="volume-up"     src="volume_up.png"     x="407" y="274"/>
+            <button keyCode="volume-down"   src="volume_down.png"   x="407" y="324"/>
+
+            <button keyCode="home"          src="home.png"          x="93"  y="602"/>
+            <button keyCode="back"          src="back.png"          x="331" y="602"/>
+            <button keyCode="dpad-up"       src="arrow_up.png"      x="185" y="608"/>
+            <button keyCode="dpad-down"     src="arrow_down.png"    x="185" y="669"/>
+            <button keyCode="dpad-left"     src="arrow_left.png"    x="155" y="610"/>
+            <button keyCode="dpad-right"    src="arrow_right.png"   x="266" y="610"/>
+            <button keyCode="dpad-center"   src="select.png"        x="186" y="637"/>
+            <button keyCode="phone-dial"    src="send.png"          x="93"  y="658"/>
+            <button keyCode="phone-hangup"  src="end.png"           x="331" y="658"/>
+            <button keyCode="soft-left"     src="menu.png"          x="192" y="569"/>
+
+<!--
+            1 {
+               image  key.png
+               x  468
+               y  302
+            }
+            2 {
+               image  key.png
+               x 505
+               y 302
+            }
+            3 {
+               image  key.png
+               x 543
+               y 302
+            }
+            4 {
+               image  key.png
+               x 579
+               y 302
+            }
+            5 {
+               image  key.png
+               x 616
+               y 302
+            }
+            6 {
+               image  key.png
+               x 653
+               y 302
+            }
+            7 {
+               image  key.png
+               x 690
+               y 302
+            }
+            8 {
+               image  key.png
+               x 727
+               y 302
+            }
+            9 {
+               image  key.png
+               x 763
+               y 302
+            }
+            0 {
+               image  key.png
+               x 801
+               y 302
+            }
+
+            q {
+               image  key.png
+               x  468
+               y  338
+            }
+            w {
+               image  key.png
+               x 505
+               y 338
+            }
+            e {
+               image  key.png
+               x 543
+               y 338
+            }
+            r {
+               image  key.png
+               x 579
+               y 338
+            }
+            t {
+               image  key.png
+               x 616
+               y 338
+            }
+            y {
+               image  key.png
+               x 653
+               y 338
+            }
+            u {
+               image  key.png
+               x 690
+               y 338
+            }
+            i {
+               image  key.png
+               x 727
+               y 338
+            }
+            o {
+               image  key.png
+               x 763
+               y 338
+            }
+            p {
+               image  key.png
+               x 801
+               y 338
+            }
+
+            a {
+               image  key.png
+               x  468
+               y  374
+            }
+            s {
+               image  key.png
+               x 505
+               y 374
+            }
+            d {
+               image  key.png
+               x 543
+               y 374
+            }
+            f {
+               image  key.png
+               x 579
+               y 374
+            }
+            g {
+               image  key.png
+               x 616
+               y 374
+            }
+            h {
+               image  key.png
+               x 653
+               y 374
+            }
+            j {
+               image  key.png
+               x 690
+               y 374
+            }
+            k {
+               image  key.png
+               x 727
+               y 374
+            }
+            l {
+               image  key.png
+               x 763
+               y 374
+            }
+            DEL {
+               image  key.png
+               x 801
+               y 374
+            }
+
+            CAP {
+               image  key.png
+               x  468
+               y  410
+            }
+            z {
+               image  key.png
+               x 505
+               y 410
+            }
+            x {
+               image  key.png
+               x 543
+               y 410
+            }
+            c {
+               image  key.png
+               x 579
+               y 410
+            }
+            v {
+               image  key.png
+               x 616
+               y 410
+            }
+            b {
+               image  key.png
+               x 653
+               y 410
+            }
+            n {
+               image  key.png
+               x 690
+               y 410
+            }
+            m {
+               image  key.png
+               x 727
+               y 410
+            }
+            PERIOD {
+               image  key.png
+               x 763
+               y 410
+            }
+            ENTER {
+               image  key.png
+               x 801
+               y 410
+            }
+
+            ALT {
+               image  key.png
+               x  468
+               y  446
+            }
+            SYM {
+               image  key.png
+               x 505
+               y 446
+            }
+            AT {
+               image  key.png
+               x 543
+               y 446
+            }
+            SPACE {
+               image  spacebar.png
+               x 579
+               y 446
+            }
+            SLASH {
+               image  key.png
+               x 727
+               y 446
+            }
+            COMMA {
+               image  key.png
+               x 763
+               y 446
+            }
+            ALT2 {
+               image  key.png
+               x 801
+               y 446
+            }
+
+            soft-left {
+                image menu.png
+                x 192
+                y 623
+            }
+            home {
+                image home.png
+                x 93
+                y 656
+            }
+            back {
+                image back.png
+                x 331
+                y 656
+            }
+            dpad-up {
+                image arrow_up.png
+                x 185
+                y 662
+            }
+            dpad-down {
+                image arrow_down.png
+                x 185
+                y 723
+            }
+            dpad-left {
+                image arrow_left.png
+                x 155
+                y 664
+            }
+            dpad-right {
+                image arrow_right.png
+                x 266
+                y 664
+            }
+            dpad-center {
+                image select.png
+                x 186
+                y 691
+            }
+            phone-dial {
+                image send.png
+                x 93
+                y 712
+            }
+            phone-hangup {
+                image end.png
+                x 331
+                y 712
+            }
+
+            power {
+                image power.png
+                x 7
+                y 120
+            }
+
+            volume-up {
+                image volume_up.png
+                x 407
+                y 328
+            }
+
+            volume-down {
+                image volume_down.png
+                x 407
+                y 378
+            }
+-->
+        </view>
+    </mode>
+
+<!--
+    <mode name="open">
+        <view display="main" x="0" y="0" rotate="90">
+        </view>
+    </mode>
+-->
+
+</device>
diff --git a/simulator/app/assets/android-dream/menu.png b/simulator/app/assets/android-dream/menu.png
new file mode 100644
index 0000000..e81d8ab
--- /dev/null
+++ b/simulator/app/assets/android-dream/menu.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/power.png b/simulator/app/assets/android-dream/power.png
new file mode 100644
index 0000000..5894288
--- /dev/null
+++ b/simulator/app/assets/android-dream/power.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/select.png b/simulator/app/assets/android-dream/select.png
new file mode 100644
index 0000000..803d493
--- /dev/null
+++ b/simulator/app/assets/android-dream/select.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/send.png b/simulator/app/assets/android-dream/send.png
new file mode 100644
index 0000000..f547c88
--- /dev/null
+++ b/simulator/app/assets/android-dream/send.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/spacebar.png b/simulator/app/assets/android-dream/spacebar.png
new file mode 100644
index 0000000..19fe604
--- /dev/null
+++ b/simulator/app/assets/android-dream/spacebar.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/volume_down.png b/simulator/app/assets/android-dream/volume_down.png
new file mode 100644
index 0000000..f8a88de
--- /dev/null
+++ b/simulator/app/assets/android-dream/volume_down.png
Binary files differ
diff --git a/simulator/app/assets/android-dream/volume_up.png b/simulator/app/assets/android-dream/volume_up.png
new file mode 100644
index 0000000..940457f
--- /dev/null
+++ b/simulator/app/assets/android-dream/volume_up.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/back.png b/simulator/app/assets/android-sooner/back.png
new file mode 100644
index 0000000..5190939
--- /dev/null
+++ b/simulator/app/assets/android-sooner/back.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/background.png b/simulator/app/assets/android-sooner/background.png
new file mode 100644
index 0000000..32534b9
--- /dev/null
+++ b/simulator/app/assets/android-sooner/background.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/down.png b/simulator/app/assets/android-sooner/down.png
new file mode 100644
index 0000000..1d602b0
--- /dev/null
+++ b/simulator/app/assets/android-sooner/down.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/end.png b/simulator/app/assets/android-sooner/end.png
new file mode 100644
index 0000000..17d8cc7
--- /dev/null
+++ b/simulator/app/assets/android-sooner/end.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/favorites.png b/simulator/app/assets/android-sooner/favorites.png
new file mode 100644
index 0000000..ad85c5c
--- /dev/null
+++ b/simulator/app/assets/android-sooner/favorites.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/home.png b/simulator/app/assets/android-sooner/home.png
new file mode 100644
index 0000000..04f9a21
--- /dev/null
+++ b/simulator/app/assets/android-sooner/home.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/imap b/simulator/app/assets/android-sooner/imap
new file mode 100644
index 0000000..64c9aff
--- /dev/null
+++ b/simulator/app/assets/android-sooner/imap
@@ -0,0 +1,15 @@
+# awk '{print $1, $4-$2 "x" $5-$3 "+" $2 "+" $3}'
+
+BACKGROUND=background-alpha.png
+
+convert $BACKGROUND -crop 43x47+55+410 send.png
+convert $BACKGROUND -crop 43x47+338+411 end.png
+convert $BACKGROUND -crop 43x40+98+393 menu.png
+convert $BACKGROUND -crop 43x40+98+433 back.png
+convert $BACKGROUND -crop 47x40+291+393 favorites.png
+convert $BACKGROUND -crop 47x40+291+433 home.png
+convert $BACKGROUND -crop 81x24+177+422 select.png
+convert $BACKGROUND -crop 81x30+177+392 up.png
+convert $BACKGROUND -crop 81x30+177+446 down.png
+convert $BACKGROUND -crop 31x84+146+392 left.png
+convert $BACKGROUND -crop 31x84+258+392 right.png
diff --git a/simulator/app/assets/android-sooner/layout.xml b/simulator/app/assets/android-sooner/layout.xml
new file mode 100644
index 0000000..d17b9d8
--- /dev/null
+++ b/simulator/app/assets/android-sooner/layout.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" ?>
+<!-- Copyright 2006 The Android Open Source Project -->
+
+<device name="Sooner">
+    <!-- title for menus -->
+    <title>Android Sooner</title>
+
+    <display name="main" width="320" height="240" format="rgb565" refresh="30"/>
+
+    <keyboard qwerty="1" keycharmap="qwerty" />
+    
+    <mode name="default">
+        <view display="main" x="53" y="95" rotate="0">
+
+            <!-- surrounding device image and "patches", drawn in order -->
+            <image src="background.png" x="0" y="0"/>
+
+            <!-- buttons for which we have highlight images -->
+            <button keyCode="soft-left"     src="menu.png"          x="93"  y="367"/>
+            <button keyCode="soft-right"    src="favorites.png"     x="286" y="367"/>
+            <button keyCode="home"          src="home.png"          x="286" y="407"/>
+            <button keyCode="back"          src="back.png"          x="93"  y="407"/>
+            <button keyCode="dpad-up"       src="up.png"            x="172" y="366"/>
+            <button keyCode="dpad-down"     src="down.png"          x="172" y="420"/>
+            <button keyCode="dpad-left"     src="left.png"          x="141" y="366"/>
+            <button keyCode="dpad-right"    src="right.png"         x="253" y="366"/>
+            <button keyCode="dpad-center"   src="select.png"        x="172" y="396"/>
+
+            <button keyCode="phone-dial"    src="send.png"          x="50"  y="384"/>
+            <button keyCode="phone-hangup"  src="end.png"           x="333" y="385"/>
+        </view>
+    </mode>
+
+</device>
diff --git a/simulator/app/assets/android-sooner/left.png b/simulator/app/assets/android-sooner/left.png
new file mode 100644
index 0000000..f712f5c
--- /dev/null
+++ b/simulator/app/assets/android-sooner/left.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/menu.png b/simulator/app/assets/android-sooner/menu.png
new file mode 100644
index 0000000..4c7f15a
--- /dev/null
+++ b/simulator/app/assets/android-sooner/menu.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/right.png b/simulator/app/assets/android-sooner/right.png
new file mode 100644
index 0000000..b1aa1f9
--- /dev/null
+++ b/simulator/app/assets/android-sooner/right.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/select.png b/simulator/app/assets/android-sooner/select.png
new file mode 100644
index 0000000..79606d1
--- /dev/null
+++ b/simulator/app/assets/android-sooner/select.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/send.png b/simulator/app/assets/android-sooner/send.png
new file mode 100644
index 0000000..9bf103d
--- /dev/null
+++ b/simulator/app/assets/android-sooner/send.png
Binary files differ
diff --git a/simulator/app/assets/android-sooner/up.png b/simulator/app/assets/android-sooner/up.png
new file mode 100644
index 0000000..324d67e
--- /dev/null
+++ b/simulator/app/assets/android-sooner/up.png
Binary files differ
diff --git a/simulator/app/assets/chimera-2000/layout.xml b/simulator/app/assets/chimera-2000/layout.xml
new file mode 100644
index 0000000..d808281
--- /dev/null
+++ b/simulator/app/assets/chimera-2000/layout.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0" ?>
+<!-- Copyright 2005 The Android Open Source Project -->
+
+<!--
+This is a silly, contrived example of a dual-screen display device.
+The "front" and "back" are shown side-by-side.  I used the phone graphics
+from two phones, which are presented as if they were glued back-to-back.
+
+This also illustrates front and back views, which for our purposes just
+determines which phone is on the left.
+
+This used to use relative paths (../whatever) to get at the phone content,
+but relative paths aren't supported in the "AssetManager" world.  We now
+use "::/whatever", which is interpreted by the simulator.  (The change from
+'.' to ':' was done to make it obvious that we weren't using UNIX paths.)
+-->
+
+
+<device name="Chimera2000">
+    <!-- title for menus -->
+    <title>Chimera WeirdPhone 2000</title>
+
+    <!-- primary display characteristics -->
+    <display name="top" width="176" height="220" format="rgb565" refresh="30"/>
+    <display name="bottom" width="176" height="220" format="rgb565" refresh="30"/>
+
+	<mode name="front">
+        <!-- the "top" device looks like a Samsung phone -->
+		<view display="top" x="49" y="73" rotate="0">
+
+			<!-- surrounding device image and "patches", drawn in order -->
+			<!-- (phone image is optional) -->
+            <image src="::/samsung-flip-2005/background-176x220.png" x="0" y="0"/>
+            <image src="::/samsung-flip-2005/home-background.png" x="22" y="426"/>
+            <image src="::/samsung-flip-2005/back-background.png" x="179" y="426"/>
+
+			<!-- buttons for which we have highlight images -->
+            <button keyCode="soft-left"     src="::/samsung-flip-2005/soft-left.png"     x="24" y="388"/>
+            <button keyCode="soft-right"    src="::/samsung-flip-2005/soft-right.png"    x="182" y="388"/>
+            <button keyCode="home"          src="::/samsung-flip-2005/home.png"          x="22" y="426"/>
+            <button keyCode="back"          src="::/samsung-flip-2005/back.png"          x="179" y="426"/>
+            <button keyCode="dpad-up"       src="::/samsung-flip-2005/arrow-up.png"      x="99" y="399"/>
+            <button keyCode="dpad-down"     src="::/samsung-flip-2005/arrow-down.png"    x="99" y="461"/>
+            <button keyCode="dpad-left"     src="::/samsung-flip-2005/arrow-left.png"    x="82" y="411"/>
+            <button keyCode="dpad-right"    src="::/samsung-flip-2005/arrow-right.png"   x="147" y="411"/>
+            <button keyCode="dpad-center"   src="::/samsung-flip-2005/select.png"        x="115" y="431"/>
+
+            <button keyCode="1"             src="::/samsung-flip-2005/1.png"             x="24" y="533"/>
+            <button keyCode="2"             src="::/samsung-flip-2005/2.png"             x="94" y="544"/>
+            <button keyCode="3"             src="::/samsung-flip-2005/3.png"             x="173" y="534"/>
+            <button keyCode="4"             src="::/samsung-flip-2005/4.png"             x="25" y="576"/>
+            <button keyCode="5"             src="::/samsung-flip-2005/5.png"             x="94" y="581"/>
+            <button keyCode="6"             src="::/samsung-flip-2005/6.png"             x="172" y="576"/>
+            <button keyCode="7"             src="::/samsung-flip-2005/7.png"             x="27" y="619"/>
+            <button keyCode="8"             src="::/samsung-flip-2005/8.png"             x="95" y="621"/>
+            <button keyCode="9"             src="::/samsung-flip-2005/9.png"             x="168" y="620"/>
+            <button keyCode="0"             src="::/samsung-flip-2005/0.png"             x="96" y="658"/>
+            <button keyCode="star"          src="::/samsung-flip-2005/star.png"          x="31" y="659"/>
+            <button keyCode="pound"         src="::/samsung-flip-2005/pound.png"         x="169" y="659"/>
+
+            <!-- buttons we haven't bothered to create highlight images for,
+                 or that aren't visible on-screen -->
+			<button keyCode="volume-up"/>
+			<button keyCode="volume-down"/>
+			<button keyCode="power"/>
+			<button keyCode="clear"/>
+		</view>
+
+        <!-- the "bottom" device looks like an HTC phone -->
+		<view display="bottom" x="36" y="73" rotate="0">
+
+			<!-- surrounding device image and "patches", drawn in order -->
+			<!-- (phone image is optional) -->
+            <image src="::/htc-audiovox-5600/background.png" x="0" y="0"/>
+
+			<!-- buttons for which we have highlight images -->
+            <button keyCode="soft-left"     src="::/htc-audiovox-5600/soft-left.png"     x="40" y="312"/>
+            <button keyCode="soft-right"    src="::/htc-audiovox-5600/soft-right.png"    x="145" y="312"/>
+            <button keyCode="home"          src="::/htc-audiovox-5600/home.png"          x="57" y="331"/>
+            <button keyCode="back"          src="::/htc-audiovox-5600/back.png"          x="123" y="333"/>
+            <button keyCode="dpad-up"       src="::/htc-audiovox-5600/arrow-up.png"      x="59" y="364"/>
+            <button keyCode="dpad-down"     src="::/htc-audiovox-5600/arrow-down.png"    x="60" y="393"/>
+            <button keyCode="dpad-left"/>
+            <button keyCode="dpad-right"/>
+            <button keyCode="dpad-center"   src="::/htc-audiovox-5600/select.png"        x="103" y="381"/>
+
+            <button keyCode="1"             src="::/htc-audiovox-5600/1.png"             x="30" y="411"/>
+            <button keyCode="2"             src="::/htc-audiovox-5600/2.png"             x="90" y="409"/>
+            <button keyCode="3"             src="::/htc-audiovox-5600/3.png"             x="159" y="413"/>
+            <button keyCode="4"             src="::/htc-audiovox-5600/4.png"             x="32" y="445"/>
+            <button keyCode="5"             src="::/htc-audiovox-5600/5.png"             x="92" y="445"/>
+            <button keyCode="6"             src="::/htc-audiovox-5600/6.png"             x="157" y="444"/>
+            <button keyCode="7"             src="::/htc-audiovox-5600/7.png"             x="28" y="476"/>
+            <button keyCode="8"             src="::/htc-audiovox-5600/8.png"             x="94" y="480"/>
+            <button keyCode="9"             src="::/htc-audiovox-5600/9.png"             x="156" y="477"/>
+            <button keyCode="0"             src="::/htc-audiovox-5600/0.png"             x="97" y="513"/>
+            <button keyCode="star"          src="::/htc-audiovox-5600/star.png"          x="45" y="509"/>
+            <button keyCode="pound"         src="::/htc-audiovox-5600/pound.png"         x="155" y="511"/>
+
+            <!-- buttons we haven't bothered to create highlight images for,
+                 or that aren't visible on-screen -->
+			<button keyCode="volume-up"/>
+			<button keyCode="volume-down"/>
+			<button keyCode="power"/>
+			<button keyCode="clear"/>
+
+		</view>
+	</mode>
+
+	<mode name="back">
+        <!-- show the back view first, then the front view -->
+		<view display="bottom" x="36" y="73" rotate="0">
+
+			<!-- surrounding device image and "patches", drawn in order -->
+			<!-- (phone image is optional) -->
+            <image src="::/htc-audiovox-5600/background.png" x="0" y="0"/>
+
+			<!-- buttons for which we have highlight images -->
+            <button keyCode="soft-left"     src="::/htc-audiovox-5600/soft-left.png"     x="40" y="312"/>
+            <button keyCode="soft-right"    src="::/htc-audiovox-5600/soft-right.png"    x="145" y="312"/>
+            <button keyCode="home"          src="::/htc-audiovox-5600/home.png"          x="57" y="331"/>
+            <button keyCode="back"          src="::/htc-audiovox-5600/back.png"          x="123" y="333"/>
+            <button keyCode="dpad-up"       src="::/htc-audiovox-5600/arrow-up.png"      x="59" y="364"/>
+            <button keyCode="dpad-down"     src="::/htc-audiovox-5600/arrow-down.png"    x="60" y="393"/>
+            <button keyCode="dpad-left"/>
+            <button keyCode="dpad-right"/>
+            <button keyCode="dpad-center"   src="::/htc-audiovox-5600/select.png"        x="103" y="381"/>
+
+            <button keyCode="1"             src="::/htc-audiovox-5600/1.png"             x="30" y="411"/>
+            <button keyCode="2"             src="::/htc-audiovox-5600/2.png"             x="90" y="409"/>
+            <button keyCode="3"             src="::/htc-audiovox-5600/3.png"             x="159" y="413"/>
+            <button keyCode="4"             src="::/htc-audiovox-5600/4.png"             x="32" y="445"/>
+            <button keyCode="5"             src="::/htc-audiovox-5600/5.png"             x="92" y="445"/>
+            <button keyCode="6"             src="::/htc-audiovox-5600/6.png"             x="157" y="444"/>
+            <button keyCode="7"             src="::/htc-audiovox-5600/7.png"             x="28" y="476"/>
+            <button keyCode="8"             src="::/htc-audiovox-5600/8.png"             x="94" y="480"/>
+            <button keyCode="9"             src="::/htc-audiovox-5600/9.png"             x="156" y="477"/>
+            <button keyCode="0"             src="::/htc-audiovox-5600/0.png"             x="97" y="513"/>
+            <button keyCode="star"          src="::/htc-audiovox-5600/star.png"          x="45" y="509"/>
+            <button keyCode="pound"         src="::/htc-audiovox-5600/pound.png"         x="155" y="511"/>
+
+            <!-- buttons we haven't bothered to create highlight images for,
+                 or that aren't visible on-screen -->
+			<button keyCode="volume-up"/>
+			<button keyCode="volume-down"/>
+			<button keyCode="power"/>
+			<button keyCode="clear"/>
+
+		</view>
+
+        <!-- our view of the device that shows the main display -->
+		<view display="top" x="49" y="73" rotate="0">
+
+			<!-- surrounding device image and "patches", drawn in order -->
+			<!-- (phone image is optional) -->
+            <image src="::/samsung-flip-2005/background-176x220.png" x="0" y="0"/>
+            <image src="::/samsung-flip-2005/home-background.png" x="22" y="426"/>
+            <image src="::/samsung-flip-2005/back-background.png" x="179" y="426"/>
+
+			<!-- buttons for which we have highlight images -->
+            <button keyCode="soft-left"     src="::/samsung-flip-2005/soft-left.png"     x="24" y="388"/>
+            <button keyCode="soft-right"    src="::/samsung-flip-2005/soft-right.png"    x="182" y="388"/>
+            <button keyCode="home"          src="::/samsung-flip-2005/home.png"          x="22" y="426"/>
+            <button keyCode="back"          src="::/samsung-flip-2005/back.png"          x="179" y="426"/>
+            <button keyCode="dpad-up"       src="::/samsung-flip-2005/arrow-up.png"      x="99" y="399"/>
+            <button keyCode="dpad-down"     src="::/samsung-flip-2005/arrow-down.png"    x="99" y="461"/>
+            <button keyCode="dpad-left"     src="::/samsung-flip-2005/arrow-left.png"    x="82" y="411"/>
+            <button keyCode="dpad-right"    src="::/samsung-flip-2005/arrow-right.png"   x="147" y="411"/>
+            <button keyCode="dpad-center"   src="::/samsung-flip-2005/select.png"        x="115" y="431"/>
+
+            <button keyCode="1"             src="::/samsung-flip-2005/1.png"             x="24" y="533"/>
+            <button keyCode="2"             src="::/samsung-flip-2005/2.png"             x="94" y="544"/>
+            <button keyCode="3"             src="::/samsung-flip-2005/3.png"             x="173" y="534"/>
+            <button keyCode="4"             src="::/samsung-flip-2005/4.png"             x="25" y="576"/>
+            <button keyCode="5"             src="::/samsung-flip-2005/5.png"             x="94" y="581"/>
+            <button keyCode="6"             src="::/samsung-flip-2005/6.png"             x="172" y="576"/>
+            <button keyCode="7"             src="::/samsung-flip-2005/7.png"             x="27" y="619"/>
+            <button keyCode="8"             src="::/samsung-flip-2005/8.png"             x="95" y="621"/>
+            <button keyCode="9"             src="::/samsung-flip-2005/9.png"             x="168" y="620"/>
+            <button keyCode="0"             src="::/samsung-flip-2005/0.png"             x="96" y="658"/>
+            <button keyCode="star"          src="::/samsung-flip-2005/star.png"          x="31" y="659"/>
+            <button keyCode="pound"         src="::/samsung-flip-2005/pound.png"         x="169" y="659"/>
+
+            <!-- buttons we haven't bothered to create highlight images for,
+                 or that aren't visible on-screen -->
+			<button keyCode="volume-up"/>
+			<button keyCode="volume-down"/>
+			<button keyCode="power"/>
+			<button keyCode="clear"/>
+		</view>
+
+	</mode>
+</device>
+
diff --git a/simulator/app/assets/danger-hiptop-m1/background-open.png b/simulator/app/assets/danger-hiptop-m1/background-open.png
new file mode 100644
index 0000000..62d2c29
--- /dev/null
+++ b/simulator/app/assets/danger-hiptop-m1/background-open.png
Binary files differ
diff --git a/simulator/app/assets/danger-hiptop-m1/background.png b/simulator/app/assets/danger-hiptop-m1/background.png
new file mode 100644
index 0000000..f8c58c7
--- /dev/null
+++ b/simulator/app/assets/danger-hiptop-m1/background.png
Binary files differ
diff --git a/simulator/app/assets/danger-hiptop-m1/layout.xml b/simulator/app/assets/danger-hiptop-m1/layout.xml
new file mode 100644
index 0000000..f8d7cba
--- /dev/null
+++ b/simulator/app/assets/danger-hiptop-m1/layout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" ?>
+<!-- Copyright 2005 The Android Open Source Project -->
+
+<device name="DangerM1">
+    <!-- title for menus -->
+    <title>Danger hiptop M1</title>
+
+    <display name="main" width="240" height="160" format="rgb565" refresh="30"/>
+
+	<keyboard qwerty="1" />
+	
+    <mode name="closed">
+        <!-- hiptop when closed -->
+        <view display="main" x="160" y="69" rotate="0">
+
+            <!-- surrounding device image and "patches", drawn in order -->
+            <image src="background.png" x="0" y="0"/>
+
+        </view>
+    </mode>
+
+    <mode name="open">
+        <!-- hiptop when flipped open -->
+        <view display="main" x="172" y="60" rotate="180">
+
+            <image src="background-open.png" x="0" y="0"/>
+        </view>
+    </mode>
+
+</device>
+
diff --git a/simulator/app/assets/htc-audiovox-5600/0.png b/simulator/app/assets/htc-audiovox-5600/0.png
new file mode 100644
index 0000000..1fa4b72
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/0.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/1.png b/simulator/app/assets/htc-audiovox-5600/1.png
new file mode 100644
index 0000000..f939334
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/1.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/2.png b/simulator/app/assets/htc-audiovox-5600/2.png
new file mode 100644
index 0000000..09be58b
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/2.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/3.png b/simulator/app/assets/htc-audiovox-5600/3.png
new file mode 100644
index 0000000..91c70c1
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/3.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/4.png b/simulator/app/assets/htc-audiovox-5600/4.png
new file mode 100644
index 0000000..da930ff
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/4.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/5.png b/simulator/app/assets/htc-audiovox-5600/5.png
new file mode 100644
index 0000000..5c4625a
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/5.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/6.png b/simulator/app/assets/htc-audiovox-5600/6.png
new file mode 100644
index 0000000..f309960
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/6.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/7.png b/simulator/app/assets/htc-audiovox-5600/7.png
new file mode 100644
index 0000000..e09b08c
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/7.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/8.png b/simulator/app/assets/htc-audiovox-5600/8.png
new file mode 100644
index 0000000..c8b3dcc
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/8.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/9.png b/simulator/app/assets/htc-audiovox-5600/9.png
new file mode 100644
index 0000000..f284212
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/9.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/arrow-down.png b/simulator/app/assets/htc-audiovox-5600/arrow-down.png
new file mode 100644
index 0000000..08a17ea
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/arrow-down.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/arrow-up.png b/simulator/app/assets/htc-audiovox-5600/arrow-up.png
new file mode 100644
index 0000000..f2998b9
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/arrow-up.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/back.png b/simulator/app/assets/htc-audiovox-5600/back.png
new file mode 100644
index 0000000..4110ec7
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/back.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/background-orange.png b/simulator/app/assets/htc-audiovox-5600/background-orange.png
new file mode 100644
index 0000000..b147546
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/background-orange.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/background.png b/simulator/app/assets/htc-audiovox-5600/background.png
new file mode 100644
index 0000000..9ffd7f3
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/background.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/home.png b/simulator/app/assets/htc-audiovox-5600/home.png
new file mode 100644
index 0000000..a10d293
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/home.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/layout.xml b/simulator/app/assets/htc-audiovox-5600/layout.xml
new file mode 100644
index 0000000..9e68c0f
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/layout.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" ?>
+<!-- Copyright 2005 The Android Open Source Project -->
+
+<device name="HTC5600">
+    <!-- title for menus -->
+    <title>HTC AudioVox 5600</title>
+
+    <!-- primary display characteristics -->
+    <display name="main" width="176" height="220" format="rgb565" refresh="30"/>
+
+	<mode name="default">
+		<view display="main" x="36" y="73" rotate="0">
+
+			<!-- surrounding device image and "patches", drawn in order -->
+			<!-- (phone image is optional) -->
+			<image src="background.png" x="0" y="0"/>
+
+			<!-- buttons for which we have highlight images -->
+			<button keyCode="soft-left"     src="soft-left.png"     x="40" y="312"/>
+			<button keyCode="soft-right"    src="soft-right.png"    x="145" y="312"/>
+			<button keyCode="home"          src="home.png"          x="57" y="331"/>
+			<button keyCode="back"          src="back.png"          x="123" y="333"/>
+			<button keyCode="dpad-up"       src="arrow-up.png"      x="59" y="364"/>
+			<button keyCode="dpad-down"     src="arrow-down.png"    x="60" y="393"/>
+            <button keyCode="dpad-left"/>
+            <button keyCode="dpad-right"/>
+			<button keyCode="dpad-center"   src="select.png"        x="103" y="381"/>
+
+			<button keyCode="1"             src="1.png"             x="30" y="411"/>
+			<button keyCode="2"             src="2.png"             x="90" y="409"/>
+			<button keyCode="3"             src="3.png"             x="159" y="413"/>
+			<button keyCode="4"             src="4.png"             x="32" y="445"/>
+			<button keyCode="5"             src="5.png"             x="92" y="445"/>
+			<button keyCode="6"             src="6.png"             x="157" y="444"/>
+			<button keyCode="7"             src="7.png"             x="28" y="476"/>
+			<button keyCode="8"             src="8.png"             x="94" y="480"/>
+			<button keyCode="9"             src="9.png"             x="156" y="477"/>
+			<button keyCode="0"             src="0.png"             x="97" y="513"/>
+			<button keyCode="star"          src="star.png"          x="45" y="509"/>
+			<button keyCode="pound"         src="pound.png"         x="155" y="511"/>
+
+            <!-- buttons we haven't bothered to create highlight images for,
+                 or that aren't visible on-screen -->
+			<button keyCode="volume-up"/>
+			<button keyCode="volume-down"/>
+			<button keyCode="power"/>
+			<button keyCode="clear"/>
+
+		</view>
+	</mode>
+
+</device>
+
diff --git a/simulator/app/assets/htc-audiovox-5600/pound.png b/simulator/app/assets/htc-audiovox-5600/pound.png
new file mode 100644
index 0000000..90df1fa
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/pound.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/select.png b/simulator/app/assets/htc-audiovox-5600/select.png
new file mode 100644
index 0000000..509082c
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/select.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/soft-left.png b/simulator/app/assets/htc-audiovox-5600/soft-left.png
new file mode 100644
index 0000000..ce8cc5c
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/soft-left.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/soft-right.png b/simulator/app/assets/htc-audiovox-5600/soft-right.png
new file mode 100644
index 0000000..f3c76c7
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/soft-right.png
Binary files differ
diff --git a/simulator/app/assets/htc-audiovox-5600/star.png b/simulator/app/assets/htc-audiovox-5600/star.png
new file mode 100644
index 0000000..19aa057
--- /dev/null
+++ b/simulator/app/assets/htc-audiovox-5600/star.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/0.png b/simulator/app/assets/htc-tornado/0.png
new file mode 100644
index 0000000..a40e898
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/0.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/1.png b/simulator/app/assets/htc-tornado/1.png
new file mode 100644
index 0000000..92c6782
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/1.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/2.png b/simulator/app/assets/htc-tornado/2.png
new file mode 100644
index 0000000..42a79ec
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/2.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/3.png b/simulator/app/assets/htc-tornado/3.png
new file mode 100644
index 0000000..7e685a8
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/3.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/4.png b/simulator/app/assets/htc-tornado/4.png
new file mode 100644
index 0000000..c1b8060
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/4.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/5.png b/simulator/app/assets/htc-tornado/5.png
new file mode 100644
index 0000000..29d2607
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/5.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/6.png b/simulator/app/assets/htc-tornado/6.png
new file mode 100644
index 0000000..509aae5
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/6.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/7.png b/simulator/app/assets/htc-tornado/7.png
new file mode 100644
index 0000000..1608777
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/7.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/8.png b/simulator/app/assets/htc-tornado/8.png
new file mode 100644
index 0000000..9fac3e6
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/8.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/9.png b/simulator/app/assets/htc-tornado/9.png
new file mode 100644
index 0000000..6a357cb
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/9.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/arrow-down.png b/simulator/app/assets/htc-tornado/arrow-down.png
new file mode 100644
index 0000000..c00787f
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/arrow-down.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/arrow-left.png b/simulator/app/assets/htc-tornado/arrow-left.png
new file mode 100644
index 0000000..1180a94
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/arrow-left.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/arrow-right.png b/simulator/app/assets/htc-tornado/arrow-right.png
new file mode 100644
index 0000000..6a511d2
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/arrow-right.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/arrow-up.png b/simulator/app/assets/htc-tornado/arrow-up.png
new file mode 100644
index 0000000..da50871
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/arrow-up.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/back.png b/simulator/app/assets/htc-tornado/back.png
new file mode 100644
index 0000000..d12273d
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/back.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/background.png b/simulator/app/assets/htc-tornado/background.png
new file mode 100644
index 0000000..09169b8
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/background.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/home.png b/simulator/app/assets/htc-tornado/home.png
new file mode 100644
index 0000000..90775a8
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/home.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/internet.png b/simulator/app/assets/htc-tornado/internet.png
new file mode 100644
index 0000000..47f1bb4
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/internet.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/layout.xml b/simulator/app/assets/htc-tornado/layout.xml
new file mode 100644
index 0000000..55c1d65
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/layout.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" ?>
+<!-- Copyright 2005 The Android Open Source Project -->
+
+<device name="HTC_Tornado">
+    <!-- title for menus -->
+    <title>HTC Tornado</title>
+
+    <!-- primary display characteristics -->
+    <display name="main" width="240" height="320" format="rgb565" refresh="30"/>
+
+    <!-- display is 33.84mm x 45.12mm -->
+
+	<mode name="default">
+		<view display="main" x="58" y="117" rotate="0">
+
+			<!-- surrounding device image and "patches", drawn in order -->
+			<!-- (phone image is optional) -->
+			<image src="background.png" x="0" y="0"/>
+
+			<!-- buttons for which we have highlight images -->
+			<button keyCode="soft-left"     src="soft-left.png"     x="49" y="450"/>
+			<button keyCode="soft-right"    src="soft-right.png"    x="249" y="450"/>
+			<button keyCode="home"          src="home.png"          x="115" y="450"/>
+			<button keyCode="back"          src="back.png"          x="182" y="450"/>
+			<button keyCode="dpad-up"       src="arrow-up.png"      x="159" y="542"/>
+			<button keyCode="dpad-down"     src="arrow-down.png"    x="160" y="573"/>
+			<button keyCode="dpad-left"       src="arrow-left.png"    x="152" y="549"/>
+			<button keyCode="dpad-right"     src="arrow-right.png"   x="183" y="549"/>
+			<button keyCode="dpad-center"   src="select.png"        x="167" y="556"/>
+
+			<button keyCode="1"             src="1.png"             x="51" y="595"/>
+			<button keyCode="2"             src="2.png"             x="133" y="595"/>
+			<button keyCode="3"             src="3.png"             x="221" y="595"/>
+			<button keyCode="4"             src="4.png"             x="51" y="633"/>
+			<button keyCode="5"             src="5.png"             x="133" y="633"/>
+			<button keyCode="6"             src="6.png"             x="221" y="633"/>
+			<button keyCode="7"             src="7.png"             x="51" y="666"/>
+			<button keyCode="8"             src="8.png"             x="133" y="666"/>
+			<button keyCode="9"             src="9.png"             x="221" y="666"/>
+			<button keyCode="0"             src="0.png"             x="133" y="699"/>
+			<button keyCode="star"          src="star.png"          x="51" y="699"/>
+			<button keyCode="pound"         src="pound.png"         x="221" y="699"/>
+
+			<button keyCode="phone-dial"	src="phone-dial.png"	x="51" y="547"/>
+			<button keyCode="phone-hangup"	src="phone-hangup.png"	x="221" y="547"/>
+
+			<button keyCode="internet"	src="internet.png"	x="52" y="490"/>
+			<button keyCode="skip-backward"	src="skip-backward.png"	x="121" y="490"/>
+			<button keyCode="pause-play"	src="pause-play.png"	x="191" y="490"/>
+			<button keyCode="skip-forward"	src="skip-forward.png"	x="260" y="490"/>
+
+
+            <!-- buttons we haven't bothered to create highlight images for,
+                 or that aren't visible on-screen -->
+			<button keyCode="volume-up"/>
+			<button keyCode="volume-down"/>
+			<button keyCode="power"/>
+			<button keyCode="clear"/>
+
+		</view>
+	</mode>
+
+</device>
+
diff --git a/simulator/app/assets/htc-tornado/pause-play.png b/simulator/app/assets/htc-tornado/pause-play.png
new file mode 100644
index 0000000..ae74d9f
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/pause-play.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/phone-dial.png b/simulator/app/assets/htc-tornado/phone-dial.png
new file mode 100644
index 0000000..30bdf9e
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/phone-dial.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/phone-hangup.png b/simulator/app/assets/htc-tornado/phone-hangup.png
new file mode 100644
index 0000000..68fe47b
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/phone-hangup.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/pound.png b/simulator/app/assets/htc-tornado/pound.png
new file mode 100644
index 0000000..92e49cb
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/pound.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/select.png b/simulator/app/assets/htc-tornado/select.png
new file mode 100644
index 0000000..a89db9a
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/select.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/skip-backward.png b/simulator/app/assets/htc-tornado/skip-backward.png
new file mode 100644
index 0000000..263b3d6
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/skip-backward.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/skip-forward.png b/simulator/app/assets/htc-tornado/skip-forward.png
new file mode 100644
index 0000000..e069db1
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/skip-forward.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/soft-left.png b/simulator/app/assets/htc-tornado/soft-left.png
new file mode 100644
index 0000000..ab6dff1
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/soft-left.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/soft-right.png b/simulator/app/assets/htc-tornado/soft-right.png
new file mode 100644
index 0000000..590982e
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/soft-right.png
Binary files differ
diff --git a/simulator/app/assets/htc-tornado/star.png b/simulator/app/assets/htc-tornado/star.png
new file mode 100644
index 0000000..9b7d79a
--- /dev/null
+++ b/simulator/app/assets/htc-tornado/star.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/back.png b/simulator/app/assets/motorola-q/back.png
new file mode 100644
index 0000000..6be2512
--- /dev/null
+++ b/simulator/app/assets/motorola-q/back.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/background.png b/simulator/app/assets/motorola-q/background.png
new file mode 100644
index 0000000..d9b30ad
--- /dev/null
+++ b/simulator/app/assets/motorola-q/background.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/down.png b/simulator/app/assets/motorola-q/down.png
new file mode 100644
index 0000000..bbe3a18
--- /dev/null
+++ b/simulator/app/assets/motorola-q/down.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/end.png b/simulator/app/assets/motorola-q/end.png
new file mode 100644
index 0000000..daff163
--- /dev/null
+++ b/simulator/app/assets/motorola-q/end.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/home.png b/simulator/app/assets/motorola-q/home.png
new file mode 100644
index 0000000..4f65f91
--- /dev/null
+++ b/simulator/app/assets/motorola-q/home.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/layout.xml b/simulator/app/assets/motorola-q/layout.xml
new file mode 100644
index 0000000..c48bce6
--- /dev/null
+++ b/simulator/app/assets/motorola-q/layout.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" ?>
+<!-- Copyright 2006 The Android Open Source Project -->
+
+<device name="MotorolaQ">
+    <!-- title for menus -->
+    <title>Motorola Q</title>
+
+    <display name="main" width="320" height="240" format="rgb565" refresh="30"/>
+
+	<keyboard qwerty="1" />
+	
+    <mode name="default">
+        <!-- hiptop when closed -->
+        <view display="main" x="58" y="121" rotate="0">
+
+            <!-- surrounding device image and "patches", drawn in order -->
+            <image src="background.png" x="0" y="0"/>
+
+			<!-- buttons for which we have highlight images -->
+			<button keyCode="soft-left"     src="menu-left.png"     x="35"  y="385"/>
+			<button keyCode="soft-right"    src="menu-right.png"    x="270" y="385"/>
+			<button keyCode="home"          src="home.png"          x="92"  y="425"/>
+			<button keyCode="back"          src="back.png"          x="264" y="425"/>
+ 			<button keyCode="dpad-up"       src="up.png"            x="166" y="400"/>
+			<button keyCode="dpad-down"     src="down.png"          x="166" y="444"/>
+			<button keyCode="dpad-left"     src="left.png"          x="158" y="406"/>
+			<button keyCode="dpad-right"    src="right.png"         x="224" y="406"/>
+			<button keyCode="dpad-center"   src="select.png"        x="194" y="419"/>
+
+			<button keyCode="phone-dial"	src="send.png"	        x="35"  y="421"/>
+			<button keyCode="phone-hangup"	src="end.png"	        x="333" y="422"/>
+        </view>
+    </mode>
+
+</device>
+
diff --git a/simulator/app/assets/motorola-q/left.png b/simulator/app/assets/motorola-q/left.png
new file mode 100644
index 0000000..3204f9b
--- /dev/null
+++ b/simulator/app/assets/motorola-q/left.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/menu-left.png b/simulator/app/assets/motorola-q/menu-left.png
new file mode 100644
index 0000000..4d1bc11
--- /dev/null
+++ b/simulator/app/assets/motorola-q/menu-left.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/menu-right.png b/simulator/app/assets/motorola-q/menu-right.png
new file mode 100644
index 0000000..58f31c6
--- /dev/null
+++ b/simulator/app/assets/motorola-q/menu-right.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/right.png b/simulator/app/assets/motorola-q/right.png
new file mode 100644
index 0000000..b6190c9
--- /dev/null
+++ b/simulator/app/assets/motorola-q/right.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/select.png b/simulator/app/assets/motorola-q/select.png
new file mode 100644
index 0000000..343fb45
--- /dev/null
+++ b/simulator/app/assets/motorola-q/select.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/send.png b/simulator/app/assets/motorola-q/send.png
new file mode 100644
index 0000000..b3ebcd5
--- /dev/null
+++ b/simulator/app/assets/motorola-q/send.png
Binary files differ
diff --git a/simulator/app/assets/motorola-q/up.png b/simulator/app/assets/motorola-q/up.png
new file mode 100644
index 0000000..7df6736
--- /dev/null
+++ b/simulator/app/assets/motorola-q/up.png
Binary files differ
diff --git a/simulator/app/assets/panasonic-x70/arrow-down.png b/simulator/app/assets/panasonic-x70/arrow-down.png
new file mode 100644
index 0000000..7ab16b4
--- /dev/null
+++ b/simulator/app/assets/panasonic-x70/arrow-down.png
Binary files differ
diff --git a/simulator/app/assets/panasonic-x70/arrow-up.png b/simulator/app/assets/panasonic-x70/arrow-up.png
new file mode 100644
index 0000000..6fb2454
--- /dev/null
+++ b/simulator/app/assets/panasonic-x70/arrow-up.png
Binary files differ
diff --git a/simulator/app/assets/panasonic-x70/back.png b/simulator/app/assets/panasonic-x70/back.png
new file mode 100644
index 0000000..04b91ef
--- /dev/null
+++ b/simulator/app/assets/panasonic-x70/back.png
Binary files differ
diff --git a/simulator/app/assets/panasonic-x70/background.png b/simulator/app/assets/panasonic-x70/background.png
new file mode 100644
index 0000000..25bc3eb
--- /dev/null
+++ b/simulator/app/assets/panasonic-x70/background.png
Binary files differ
diff --git a/simulator/app/assets/panasonic-x70/home.png b/simulator/app/assets/panasonic-x70/home.png
new file mode 100644
index 0000000..12ac7bb
--- /dev/null
+++ b/simulator/app/assets/panasonic-x70/home.png
Binary files differ
diff --git a/simulator/app/assets/panasonic-x70/layout.xml b/simulator/app/assets/panasonic-x70/layout.xml
new file mode 100644
index 0000000..0cb8bbd
--- /dev/null
+++ b/simulator/app/assets/panasonic-x70/layout.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" ?>
+<!-- Copyright 2005 The Android Open Source Project -->
+
+<device name="PanasonicX70">
+    <!-- title for menus -->
+    <title>Panasonic X70</title>
+
+    <display name="main" width="176" height="220" format="rgb565" refresh="30"/>
+
+	<mode name="default">
+		<!-- primary display characteristics -->
+		<view display="main" x="39" y="43" rotate="0">
+
+			<!-- surrounding device image and "patches", drawn in order -->
+			<!-- (phone image is optional) -->
+			<image src="background.png" x="0" y="0"/>
+
+			<button keyCode="soft-left"     src="soft-left.png"     x="45" y="324"/>
+			<button keyCode="soft-right"    src="soft-right.png"    x="146" y="324"/>
+			<button keyCode="home"          src="home.png"          x="21" y="345"/>
+			<button keyCode="back"          src="back.png"          x="180" y="347"/>
+			<button keyCode="dpad-up"       src="arrow-up.png"      x="88" y="348"/>
+			<button keyCode="dpad-down"     src="arrow-down.png"    x="95" y="414"/>
+            <button keyCode="dpad-left"/>
+            <button keyCode="dpad-right"/>
+			<button keyCode="dpad-center"   src="select.png"        x="101" y="377"/>
+
+            <button keyCode="1"/>
+            <button keyCode="2"/>
+            <button keyCode="3"/>
+            <button keyCode="4"/>
+            <button keyCode="5"/>
+            <button keyCode="6"/>
+            <button keyCode="7"/>
+            <button keyCode="8"/>
+            <button keyCode="9"/>
+            <button keyCode="0"/>
+            <button keyCode="star"/>
+            <button keyCode="pound"/>
+
+			<button keyCode="volume-up"/>
+			<button keyCode="volume-down"/>
+			<button keyCode="power"/>
+			<button keyCode="clear"/>
+
+		</view>
+	</mode>
+
+</device>
+
diff --git a/simulator/app/assets/panasonic-x70/select.png b/simulator/app/assets/panasonic-x70/select.png
new file mode 100644
index 0000000..993ecad
--- /dev/null
+++ b/simulator/app/assets/panasonic-x70/select.png
Binary files differ
diff --git a/simulator/app/assets/panasonic-x70/soft-left.png b/simulator/app/assets/panasonic-x70/soft-left.png
new file mode 100644
index 0000000..c384bb7
--- /dev/null
+++ b/simulator/app/assets/panasonic-x70/soft-left.png
Binary files differ
diff --git a/simulator/app/assets/panasonic-x70/soft-right.png b/simulator/app/assets/panasonic-x70/soft-right.png
new file mode 100644
index 0000000..948e40f
--- /dev/null
+++ b/simulator/app/assets/panasonic-x70/soft-right.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/0.png b/simulator/app/assets/samsung-flip-2005/0.png
new file mode 100644
index 0000000..839e6d1
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/0.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/1.png b/simulator/app/assets/samsung-flip-2005/1.png
new file mode 100644
index 0000000..499cf6c
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/1.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/2.png b/simulator/app/assets/samsung-flip-2005/2.png
new file mode 100644
index 0000000..93207cc
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/2.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/3.png b/simulator/app/assets/samsung-flip-2005/3.png
new file mode 100644
index 0000000..499efdb
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/3.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/4.png b/simulator/app/assets/samsung-flip-2005/4.png
new file mode 100644
index 0000000..775b15d
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/4.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/5.png b/simulator/app/assets/samsung-flip-2005/5.png
new file mode 100644
index 0000000..b8a5723
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/5.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/6.png b/simulator/app/assets/samsung-flip-2005/6.png
new file mode 100644
index 0000000..b6e0de9
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/6.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/7.png b/simulator/app/assets/samsung-flip-2005/7.png
new file mode 100644
index 0000000..5416186
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/7.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/8.png b/simulator/app/assets/samsung-flip-2005/8.png
new file mode 100644
index 0000000..2dfcae3
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/8.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/9.png b/simulator/app/assets/samsung-flip-2005/9.png
new file mode 100644
index 0000000..6c11eed
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/9.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/abc.png b/simulator/app/assets/samsung-flip-2005/abc.png
new file mode 100644
index 0000000..9f43afe
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/abc.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/arrow-down.png b/simulator/app/assets/samsung-flip-2005/arrow-down.png
new file mode 100644
index 0000000..0f7ea7b
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/arrow-down.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/arrow-left.png b/simulator/app/assets/samsung-flip-2005/arrow-left.png
new file mode 100644
index 0000000..ad1982f
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/arrow-left.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/arrow-right.png b/simulator/app/assets/samsung-flip-2005/arrow-right.png
new file mode 100644
index 0000000..bf9cf48
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/arrow-right.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/arrow-up.png b/simulator/app/assets/samsung-flip-2005/arrow-up.png
new file mode 100644
index 0000000..ab62e33
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/arrow-up.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/back-background.png b/simulator/app/assets/samsung-flip-2005/back-background.png
new file mode 100644
index 0000000..741db99
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/back-background.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/back.png b/simulator/app/assets/samsung-flip-2005/back.png
new file mode 100644
index 0000000..9eb9a17
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/back.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/background-176_208.png b/simulator/app/assets/samsung-flip-2005/background-176_208.png
new file mode 100644
index 0000000..1ed1248
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/background-176_208.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/background-176x220.png b/simulator/app/assets/samsung-flip-2005/background-176x220.png
new file mode 100644
index 0000000..361304e
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/background-176x220.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/clear.png b/simulator/app/assets/samsung-flip-2005/clear.png
new file mode 100644
index 0000000..f9c421c
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/clear.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/end.png b/simulator/app/assets/samsung-flip-2005/end.png
new file mode 100644
index 0000000..5d0056c
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/end.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/home-background.png b/simulator/app/assets/samsung-flip-2005/home-background.png
new file mode 100644
index 0000000..3ceee9c
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/home-background.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/home.png b/simulator/app/assets/samsung-flip-2005/home.png
new file mode 100644
index 0000000..8510b7e
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/home.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/layout.xml b/simulator/app/assets/samsung-flip-2005/layout.xml
new file mode 100644
index 0000000..3bc2ddd
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/layout.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" ?>
+<!-- Copyright 2005 The Android Open Source Project -->
+
+<device name="Samsung1234">
+    <!-- title for menus -->
+    <title>Samsung Flip Phone</title>
+
+    <!-- primary display characteristics -->
+    <display name="main" width="176" height="220" format="rgb565" refresh="30"/>
+
+	<mode name="default">
+        <!-- our view of the device that shows the main display -->
+		<view display="main" x="49" y="73" rotate="0">
+
+			<!-- surrounding device image and "patches", drawn in order -->
+			<!-- (phone image is optional) -->
+			<image src="background-176x220.png" x="0" y="0"/>
+			<image src="home-background.png" x="22" y="426"/>
+			<image src="back-background.png" x="179" y="426"/>
+
+			<!-- buttons for which we have highlight images -->
+			<button keyCode="soft-left"     src="soft-left.png"     x="24" y="388"/>
+			<button keyCode="soft-right"    src="soft-right.png"    x="182" y="388"/>
+			<button keyCode="home"          src="home.png"          x="22" y="426"/>
+			<button keyCode="back"          src="back.png"          x="179" y="426"/>
+			<button keyCode="dpad-up"       src="arrow-up.png"      x="99" y="399"/>
+			<button keyCode="dpad-down"     src="arrow-down.png"    x="99" y="461"/>
+			<button keyCode="dpad-left"     src="arrow-left.png"    x="82" y="411"/>
+			<button keyCode="dpad-right"    src="arrow-right.png"   x="147" y="411"/>
+			<button keyCode="dpad-center"   src="select.png"        x="115" y="431"/>
+
+			<button keyCode="1"             src="1.png"             x="24" y="533"/>
+			<button keyCode="2"             src="2.png"             x="94" y="544"/>
+			<button keyCode="3"             src="3.png"             x="173" y="534"/>
+			<button keyCode="4"             src="4.png"             x="25" y="576"/>
+			<button keyCode="5"             src="5.png"             x="94" y="581"/>
+			<button keyCode="6"             src="6.png"             x="172" y="576"/>
+			<button keyCode="7"             src="7.png"             x="27" y="619"/>
+			<button keyCode="8"             src="8.png"             x="95" y="621"/>
+			<button keyCode="9"             src="9.png"             x="168" y="620"/>
+			<button keyCode="0"             src="0.png"             x="96" y="658"/>
+			<button keyCode="star"          src="star.png"          x="31" y="659"/>
+			<button keyCode="pound"         src="pound.png"         x="169" y="659"/>
+
+            <!-- buttons we haven't bothered to create highlight images for,
+                 or that aren't visible on-screen -->
+			<button keyCode="volume-up"/>
+			<button keyCode="volume-down"/>
+			<button keyCode="power"/>
+			<button keyCode="clear"/>
+
+		</view>
+	</mode>
+
+</device>
+
diff --git a/simulator/app/assets/samsung-flip-2005/menu.png b/simulator/app/assets/samsung-flip-2005/menu.png
new file mode 100644
index 0000000..7ac077c
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/menu.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/pound.png b/simulator/app/assets/samsung-flip-2005/pound.png
new file mode 100644
index 0000000..22440bc
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/pound.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/select.png b/simulator/app/assets/samsung-flip-2005/select.png
new file mode 100644
index 0000000..1c735f8
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/select.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/send.png b/simulator/app/assets/samsung-flip-2005/send.png
new file mode 100644
index 0000000..23cea52
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/send.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/soft-left.png b/simulator/app/assets/samsung-flip-2005/soft-left.png
new file mode 100644
index 0000000..224d281
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/soft-left.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/soft-right.png b/simulator/app/assets/samsung-flip-2005/soft-right.png
new file mode 100644
index 0000000..a8eec97
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/soft-right.png
Binary files differ
diff --git a/simulator/app/assets/samsung-flip-2005/star.png b/simulator/app/assets/samsung-flip-2005/star.png
new file mode 100644
index 0000000..457348d
--- /dev/null
+++ b/simulator/app/assets/samsung-flip-2005/star.png
Binary files differ
diff --git a/simulator/app/help/unnamed.htb b/simulator/app/help/unnamed.htb
new file mode 100644
index 0000000..3933e69
--- /dev/null
+++ b/simulator/app/help/unnamed.htb
Binary files differ
diff --git a/simulator/wrapsim/Android.mk b/simulator/wrapsim/Android.mk
new file mode 100644
index 0000000..7ac8359
--- /dev/null
+++ b/simulator/wrapsim/Android.mk
@@ -0,0 +1,53 @@
+# Copyright 2007 The Android Open Source Project
+
+#
+# Build instructions for simulator LD_PRELOAD wrapper.
+#
+ifneq ($(TARGET_ARCH),arm)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	BitVector.c \
+	DevAudio.c \
+	DevConsoleTty.c \
+	DevEvent.c \
+	DevFb.c \
+	DevLog.c \
+	DevPower.c \
+	DevVibrator.c \
+	FakeDev.c \
+	Init.c \
+	Intercept.c \
+	Log.c \
+	SimMgr.c
+
+LOCAL_C_INCLUDES += prebuilt/common/esd
+
+LOCAL_MODULE := libwrapsim
+
+# Relying on other Android libraries is probably a bad idea, since any
+# library or system calls they make could lead to recursive behavior.
+#
+#LOCAL_SHARED_LIBRARIES +=
+
+LOCAL_LDLIBS += -lpthread -ldl -lesd
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+
+#
+# Build instructions for simulator runtime launch wrapper.
+#
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	LaunchWrapper.c
+
+LOCAL_MODULE := launch-wrapper
+include $(BUILD_EXECUTABLE)
+
+endif
+# ifneq ($(TARGET_ARCH),arm)
diff --git a/simulator/wrapsim/BitVector.c b/simulator/wrapsim/BitVector.c
new file mode 100644
index 0000000..aaa1408
--- /dev/null
+++ b/simulator/wrapsim/BitVector.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Simple bit vector.
+ */
+#include "Common.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#define kBitVectorGrowth    4   /* increase by 4 uint32_t when limit hit */
+
+
+/*
+ * Allocate a bit vector with enough space to hold at least the specified
+ * number of bits.
+ */
+BitVector* wsAllocBitVector(int startBits, int isExpandable)
+{
+    BitVector* bv;
+    int count;
+
+    assert(sizeof(bv->storage[0]) == 4);        /* assuming 32-bit units */
+    assert(startBits > 0);
+
+    bv = (BitVector*) malloc(sizeof(BitVector));
+
+    count = (startBits + 31) >> 5;
+
+    bv->storageSize = count;
+    bv->isExpandable = isExpandable;
+    bv->storage = (uint32_t*) malloc(count * sizeof(uint32_t));
+    memset(bv->storage, 0xff, count * sizeof(uint32_t));
+    return bv;
+}
+
+/*
+ * Free a BitVector.
+ */
+void wsFreeBitVector(BitVector* pBits)
+{
+    if (pBits == NULL)
+        return;
+
+    free(pBits->storage);
+    free(pBits);
+}
+
+/*
+ * "Allocate" the first-available bit in the bitmap.
+ *
+ * This is not synchronized.  The caller is expected to hold some sort of
+ * lock that prevents multiple threads from executing simultaneously in
+ * dvmAllocBit/dvmFreeBit.
+ *
+ * The bitmap indicates which resources are free, so we use '1' to indicate
+ * available and '0' to indicate allocated.
+ */
+int wsAllocBit(BitVector* pBits)
+{
+    int word, bit;
+
+retry:
+    for (word = 0; word < pBits->storageSize; word++) {
+        if (pBits->storage[word] != 0) {
+            /*
+             * There are unallocated bits in this word.  Return the first.
+             */
+            bit = ffs(pBits->storage[word]) -1;
+            assert(bit >= 0 && bit < 32);
+            pBits->storage[word] &= ~(1 << bit);
+            return (word << 5) | bit;
+        }
+    }
+
+    /*
+     * Ran out of space, allocate more if we're allowed to.
+     */
+    if (!pBits->isExpandable)
+        return -1;
+
+    pBits->storage = realloc(pBits->storage,
+                    (pBits->storageSize + kBitVectorGrowth) * sizeof(uint32_t));
+    memset(&pBits->storage[pBits->storageSize], 0xff,
+        kBitVectorGrowth * sizeof(uint32_t));
+    pBits->storageSize += kBitVectorGrowth;
+    goto retry;
+}
+
+/*
+ * Mark the specified bit as "free".
+ */
+void wsFreeBit(BitVector* pBits, int num)
+{
+    assert(num >= 0 &&
+           num < (int) pBits->storageSize * (int)sizeof(uint32_t) * 8);
+
+    pBits->storage[num >> 5] |= 1 << (num & 0x1f);
+}
+
diff --git a/simulator/wrapsim/BitVector.h b/simulator/wrapsim/BitVector.h
new file mode 100644
index 0000000..048cf91
--- /dev/null
+++ b/simulator/wrapsim/BitVector.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Simple bit vector.
+ */
+#ifndef _WRAPSIM_BITVECTOR_H
+#define _WRAPSIM_BITVECTOR_H
+
+#include <stdint.h>
+
+/*
+ * Expanding bitmap, used for tracking resources.  Bits are numbered starting
+ * from zero.
+ */
+typedef struct BitVector {
+    int         isExpandable;   /* expand bitmap if we run out? */
+    int         storageSize;    /* current size, in 32-bit words */
+    uint32_t*   storage;
+} BitVector;
+
+/* allocate a bit vector with enough space to hold "startBits" bits */
+BitVector* wsAllocBitVector(int startBits, int isExpandable);
+void wsFreeBitVector(BitVector* pBits);
+
+/*
+ * Set/clear a single bit; assumes external synchronization.
+ *
+ * We always allocate the first possible bit.  If we run out of space in
+ * the bitmap, and it's not marked expandable, dvmAllocBit returns -1.
+ */
+int wsAllocBit(BitVector* pBits);
+void wsFreeBit(BitVector* pBits, int num);
+
+#endif /*_WRAPSIM_BITVECTOR_H*/
diff --git a/simulator/wrapsim/Common.h b/simulator/wrapsim/Common.h
new file mode 100644
index 0000000..a9c3bb8
--- /dev/null
+++ b/simulator/wrapsim/Common.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Common defines and includes.
+ */
+#ifndef _WRAPSIM_COMMON_H
+#define _WRAPSIM_COMMON_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "BitVector.h"
+#include "FakeDev.h"
+#include "Log.h"
+#include "SimMgr.h"
+#include "Globals.h"
+
+#endif /*_WRAPSIM_COMMON_H*/
diff --git a/simulator/wrapsim/DevAudio.c b/simulator/wrapsim/DevAudio.c
new file mode 100644
index 0000000..0f52b81
--- /dev/null
+++ b/simulator/wrapsim/DevAudio.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Audio output device.
+ */
+#include "Common.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <esd.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/input.h>
+
+
+/*
+ * Input event device state.
+ */
+typedef struct AudioState {
+    int fd;
+    int sourceId;
+    int esdVol;
+    int streamType;
+} AudioState;
+
+/*
+ * Set some stuff up.
+ */
+static int configureInitialState(const char* pathName, AudioState* audioState)
+{
+    esd_player_info_t *pi; 
+    audioState->fd = -1;
+    audioState->sourceId = -1;
+    audioState->esdVol = -1;
+    audioState->streamType = 0;
+
+    int format = ESD_BITS16 | ESD_STEREO | ESD_STREAM | ESD_PLAY;
+    char namestring[] = "Android Audio XXXXXXXX";
+    sprintf(namestring,"Android Audio %08x", (unsigned int)audioState);
+    int esd_fd = esd_play_stream_fallback(format, 44100, NULL, namestring);
+    if (esd_fd > 0) {
+        // find the source_id for this stream
+        int mix = esd_open_sound(NULL);
+        if (mix > 0) {
+            esd_info_t *info = esd_get_all_info(mix);
+
+            if (info) {
+                for(pi = info->player_list; pi; pi = pi->next) {
+                    if(strcmp(pi->name, namestring) == 0) {
+                        audioState->sourceId = pi->source_id;
+                        break;
+                    }
+                }
+                esd_free_all_info(info);
+            }
+            esd_close(mix);
+        }
+        audioState->fd = esd_fd;
+        return 0;
+    }
+    printf("Couldn't open audio device. Faking it.\n");
+    return 0;
+}
+
+/*
+ * Return the next available input event.
+ *
+ * We just pass this through to the real "write", since "fd" is real.
+ */
+static ssize_t writeAudio(FakeDev* dev, int fd, const void* buf, size_t count)
+{
+    AudioState *state = (AudioState*)dev->state;
+    if (state->fd >= 0)
+        return _ws_write(state->fd, buf, count);
+
+    // fake timing
+    usleep(count * 10000 / 441 * 4);
+    return count;
+}
+
+/*
+ * Handle event ioctls.
+ */
+static int ioctlAudio(FakeDev* dev, int fd, int request, void* argp)
+{
+    return -1;
+}
+
+/*
+ * Free up our state before closing down the fake descriptor.
+ */
+static int closeAudio(FakeDev* dev, int fd)
+{
+    AudioState *state = (AudioState*)dev->state;
+    close(state->fd);
+    free(state);
+    dev->state = NULL;
+    return 0;
+}
+
+/*
+ * Open an audio output device.
+ */
+FakeDev* wsOpenDevAudio(const char* pathName, int flags)
+{
+    FakeDev* newDev = wsCreateFakeDev(pathName);
+    if (newDev != NULL) {
+        newDev->write = writeAudio;
+        newDev->ioctl = ioctlAudio;
+        newDev->close = closeAudio;
+
+        AudioState* eventState = calloc(1, sizeof(AudioState));
+
+        if (configureInitialState(pathName, eventState) != 0) {
+            free(eventState);
+            return NULL;
+        }
+        newDev->state = eventState;
+    }
+
+    return newDev;
+}
diff --git a/simulator/wrapsim/DevConsoleTty.c b/simulator/wrapsim/DevConsoleTty.c
new file mode 100644
index 0000000..166d648
--- /dev/null
+++ b/simulator/wrapsim/DevConsoleTty.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Console tty device.
+ */
+#include "Common.h"
+
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <linux/vt.h>
+
+
+/*
+ * Handle the various console ioctls, most of which we can just ignore.
+ */
+static int ioctlConsoleTty(FakeDev* dev, int fd, int request, void* argp)
+{
+    wsLog("%s: ioctl(0x%x, %p)\n", dev->debugName, request, argp);
+    switch (request) {
+    case VT_GETSTATE:       // struct vt_stat*
+        /*
+         * Looks like they want vs.v_active.  This just gets fed back into
+         * another console ioctl, so we don't really need to do anything.
+         * We zero out the struct so the data will at least appear to be
+         * initialized.
+         */
+        memset(argp, 0, sizeof(struct vt_stat));
+        break;
+    case VT_OPENQRY:        // int*
+        /* they want the console number */
+        *(int*)argp = 123;
+        break;
+    default:
+        /* ignore anything we don't understand */
+        break;
+    }
+
+    return 0;
+}
+
+/*
+ * Open the console TTY device, which responds to a collection of ioctl()s.
+ */
+FakeDev* wsOpenDevConsoleTty(const char* pathName, int flags)
+{
+    FakeDev* newDev = wsCreateFakeDev(pathName);
+    if (newDev != NULL) {
+        newDev->ioctl = ioctlConsoleTty;
+    }
+    return newDev;
+}
+
diff --git a/simulator/wrapsim/DevEvent.c b/simulator/wrapsim/DevEvent.c
new file mode 100644
index 0000000..692856e
--- /dev/null
+++ b/simulator/wrapsim/DevEvent.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Input event device.
+ */
+#include "Common.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/input.h>
+
+
+/*
+ * Input event device state.
+ */
+typedef struct EventState {
+    struct input_id ident;
+
+    char*   name;
+    char*   location;
+    char*   idstr;
+    int     protoVersion;
+} EventState;
+
+/*
+ * Key bit mask, for EVIOCGBIT(EV_KEY).
+ *
+ * (For now, just pretend to be a "goldfish" like the emulator.)
+ */
+static const unsigned char gKeyBitMask[64] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+};
+
+/*
+ * Set some stuff up.
+ */
+static void configureInitialState(const char* pathName, EventState* eventState)
+{
+    /*
+     * Swim like a goldfish.
+     */
+    eventState->ident.bustype = 0;
+    eventState->ident.vendor = 0;
+    eventState->ident.product = 0;
+    eventState->ident.version = 0;
+
+    eventState->name = strdup(gWrapSim.keyMap);
+    eventState->location = strdup("");
+    eventState->idstr = strdup("");
+    eventState->protoVersion = 0x010000;
+}
+
+/*
+ * Free up the state structure.
+ */
+static void freeState(EventState* eventState)
+{
+    if (eventState != NULL) {
+        free(eventState->name);
+        free(eventState->location);
+        free(eventState->idstr);
+        free(eventState);
+    }
+}
+
+/*
+ * Handle one of the EVIOCGABS requests.
+ *
+ * Currently not doing much here.
+ */
+static void handleAbsGet(int reqIdx, void* argp)
+{
+    struct input_absinfo info;
+
+    switch (reqIdx) {
+    case ABS_X:
+        wsLog("  req for abs X\n");
+        break;
+    case ABS_Y:
+        wsLog("  req for abs Y\n");
+        break;
+    case ABS_PRESSURE:
+        wsLog("  req for abs PRESSURE\n");
+        break;
+    case ABS_TOOL_WIDTH:
+        wsLog("  req for abs TOOL_WIDTH\n");
+        break;
+    default:
+        wsLog("  req for unexpected event abs 0x%02x\n", reqIdx);
+        break;
+    }
+
+    memset(&info, 0, sizeof(info));
+    memcpy(argp, &info, sizeof(struct input_absinfo));
+}
+
+/*
+ * Return the next available input event.
+ *
+ * We just pass this through to the real "read", since "fd" is real.
+ */
+static ssize_t readEvent(FakeDev* dev, int fd, void* buf, size_t count)
+{
+    return _ws_read(fd, buf, count);
+}
+
+/*
+ * Somebody is trying to write to the event pipe.  This can be used to set
+ * the state of LED.
+ */
+static ssize_t writeEvent(FakeDev* dev, int fd, const void* buf, size_t count)
+{
+    const struct input_event* piev;
+
+    if (count == sizeof(*piev)) {
+        piev = (const struct input_event*) buf;
+
+        if (piev->type == EV_LED) {
+            wsLog("%s: set LED code=%d value=%d\n",
+                dev->debugName, piev->code, piev->value);
+        } else {
+            wsLog("%s: writeEvent got %d bytes, type=%d\n",
+                dev->debugName, count, piev->type);
+        }
+    } else {
+        wsLog("%s: warning: writeEvent got %d bytes, not sure why\n",
+            dev->debugName, count);
+    }
+
+    return count;
+}
+
+/*
+ * Handle event ioctls.
+ */
+static int ioctlEvent(FakeDev* dev, int fd, int request, void* argp)
+{
+    EventState* state = (EventState*) dev->state;
+    unsigned int urequest = (unsigned int) request;
+
+    wsLog("%s: ioctl(0x%x, %p)\n", dev->debugName, urequest, argp);
+
+    if (_IOC_TYPE(urequest) != _IOC_TYPE(EVIOCGVERSION)) {
+        wsLog("%s: inappropriate ioctl 0x%08x\n", dev->debugName, urequest);
+        return -1;
+    }
+
+    if (urequest == EVIOCGVERSION) {
+        *(int*)argp = state->protoVersion;
+    } else if (urequest == EVIOCGID) {
+        memcpy(argp, &state->ident, sizeof(struct input_id));
+    } else if (_IOC_NR(urequest) == _IOC_NR(EVIOCGNAME(0))) {
+        int maxLen = _IOC_SIZE(urequest);
+        int strLen = (int) strlen(state->name);
+        if (strLen >= maxLen) {
+            errno = EINVAL;
+            return -1;
+        }
+        memcpy(argp, state->name, strLen+1);
+        return strLen;
+    } else if (_IOC_NR(urequest) == _IOC_NR(EVIOCGPHYS(0))) {
+        int maxLen = _IOC_SIZE(urequest);
+        int strLen = (int) strlen(state->location);
+        if (strLen >= maxLen) {
+            errno = EINVAL;
+            return -1;
+        }
+        memcpy(argp, state->location, strLen+1);
+        return strLen;
+    } else if (_IOC_NR(urequest) == _IOC_NR(EVIOCGUNIQ(0))) {
+        /* device doesn't seem to support this, neither will we */
+        return -1;
+    } else if (_IOC_NR(urequest) == _IOC_NR(EVIOCGBIT(EV_KEY,0))) {
+        /* keys */
+        int maxLen = _IOC_SIZE(urequest);
+        if (maxLen > (int) sizeof(gKeyBitMask))
+            maxLen = sizeof(gKeyBitMask);
+        memcpy(argp, gKeyBitMask, maxLen);
+    } else if (_IOC_NR(urequest) == _IOC_NR(EVIOCGBIT(EV_REL,0))) {
+        /* relative controllers (trackball) */
+        int maxLen = _IOC_SIZE(urequest);
+        memset(argp, 0xff, maxLen);
+    } else if (!getenv("NOTOUCH") && _IOC_NR(urequest) == _IOC_NR(EVIOCGBIT(EV_ABS,0))) {
+        // absolute controllers (touch screen)
+        int maxLen = _IOC_SIZE(urequest);
+        memset(argp, 0xff, maxLen);
+
+    } else if (_IOC_NR(urequest) >= _IOC_NR(EVIOCGABS(ABS_X)) &&
+               _IOC_NR(urequest) <= _IOC_NR(EVIOCGABS(ABS_MAX)))
+    {
+        /* get abs value / limits */
+        int reqIdx = _IOC_NR(urequest) - _IOC_NR(EVIOCGABS(ABS_X));
+        handleAbsGet(reqIdx, argp);
+    } else {
+        wsLog("GLITCH: UNKNOWN ioctl request 0x%x on %s\n",
+            urequest, dev->debugName);
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Free up our state before closing down the fake descriptor.
+ */
+static int closeEvent(FakeDev* dev, int fd)
+{
+    freeState((EventState*)dev->state);
+    dev->state = NULL;
+    if (gWrapSim.keyInputDevice == dev) {
+        gWrapSim.keyInputDevice = NULL;
+        wsLog("Sim input device closed\n");
+    }
+    return 0;
+}
+
+/*
+ * Open an input event device.
+ */
+FakeDev* wsOpenDevEvent(const char* pathName, int flags)
+{
+    FakeDev* newDev = wsCreateRealFakeDev(pathName);
+    if (newDev != NULL) {
+        newDev->read = readEvent;
+        newDev->write = writeEvent;
+        newDev->ioctl = ioctlEvent;
+        newDev->close = closeEvent;
+
+        EventState* eventState = calloc(1, sizeof(EventState));
+
+        configureInitialState(pathName, eventState);
+        newDev->state = eventState;
+
+        /*
+         * First one opened becomes the place where we queue up input
+         * events from the simulator.  This approach will fail if the
+         * app opens the device, then opens it a second time for input,
+         * then closes the first.  The app doesn't currently do this (though
+         * it does do quick opens to fiddle with LEDs).
+         */
+        if (gWrapSim.keyInputDevice == NULL) {
+            gWrapSim.keyInputDevice = newDev;
+            wsLog("Device %p / %d will receive sim input events\n",
+                newDev, newDev->fd);
+        }
+    }
+
+    return newDev;
+}
+
+/*
+ * Write a key event.
+ */
+static int sendKeyEvent(FakeDev* dev, int code, int isDown)
+{
+    struct input_event iev;
+    ssize_t actual;
+
+    gettimeofday(&iev.time, NULL);
+    iev.type = EV_KEY;
+    iev.code = code;
+    iev.value = (isDown != 0) ? 1 : 0;
+
+    actual = _ws_write(dev->otherFd, &iev, sizeof(iev));
+    if (actual != (ssize_t) sizeof(iev)) {
+        wsLog("WARNING: send key event partial write (%d of %d)\n",
+            actual, sizeof(iev));
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Write an absolute (touch screen) event.
+ */
+static int sendAbsButton(FakeDev* dev, int x, int y, int isDown)
+{
+    struct input_event iev;
+    ssize_t actual;
+
+    wsLog("absButton x=%d y=%d down=%d\n", x, y, isDown);
+
+    gettimeofday(&iev.time, NULL);
+    iev.type = EV_KEY;
+    iev.code = BTN_TOUCH;
+    iev.value = (isDown != 0) ? 1 : 0;
+
+    actual = _ws_write(dev->otherFd, &iev, sizeof(iev));
+    if (actual != (ssize_t) sizeof(iev)) {
+        wsLog("WARNING: send touch event partial write (%d of %d)\n",
+            actual, sizeof(iev));
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Write an absolute (touch screen) event.
+ */
+static int sendAbsMovement(FakeDev* dev, int x, int y)
+{
+    struct input_event iev;
+    ssize_t actual;
+
+    wsLog("absMove x=%d y=%d\n", x, y);
+
+    gettimeofday(&iev.time, NULL);
+    iev.type = EV_ABS;
+    iev.code = ABS_X;
+    iev.value = x;
+
+    actual = _ws_write(dev->otherFd, &iev, sizeof(iev));
+    if (actual != (ssize_t) sizeof(iev)) {
+        wsLog("WARNING: send abs movement event partial X write (%d of %d)\n",
+            actual, sizeof(iev));
+        return -1;
+    }
+
+    iev.code = ABS_Y;
+    iev.value = y;
+
+    actual = _ws_write(dev->otherFd, &iev, sizeof(iev));
+    if (actual != (ssize_t) sizeof(iev)) {
+        wsLog("WARNING: send abs movement event partial Y write (%d of %d)\n",
+            actual, sizeof(iev));
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Not quite sure what this is for, but the emulator does it.
+ */
+static int sendAbsSyn(FakeDev* dev)
+{
+    struct input_event iev;
+    ssize_t actual;
+
+    gettimeofday(&iev.time, NULL);
+    iev.type = EV_SYN;
+    iev.code = 0;
+    iev.value = 0;
+
+    actual = _ws_write(dev->otherFd, &iev, sizeof(iev));
+    if (actual != (ssize_t) sizeof(iev)) {
+        wsLog("WARNING: send abs movement syn (%d of %d)\n",
+            actual, sizeof(iev));
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Send a key event to the fake key event device.
+ *
+ * We have to translate the simulator key event into one or more device
+ * key events.
+ */
+void wsSendSimKeyEvent(int key, int isDown)
+{
+    FakeDev* dev;
+    EventState* state;
+
+    dev = gWrapSim.keyInputDevice;
+    if (dev == NULL)
+        return;
+
+    sendKeyEvent(dev, key, isDown);
+}
+
+/*
+ * Send a touch-screen event to the fake key event device.
+ *
+ * We have to translate the simulator key event into one or more device
+ * key events.
+ */
+void wsSendSimTouchEvent(int action, int x, int y)
+{
+    FakeDev* dev;
+    EventState* state;
+
+    dev = gWrapSim.keyInputDevice;
+    if (dev == NULL)
+        return;
+
+    if (action == kTouchDown) {
+        sendAbsMovement(dev, x, y);
+        sendAbsButton(dev, x, y, 1);
+        sendAbsSyn(dev);
+    } else if (action == kTouchUp) {
+        sendAbsButton(dev, x, y, 0);
+        sendAbsSyn(dev);
+    } else if (action == kTouchDrag) {
+        sendAbsMovement(dev, x, y);
+        sendAbsSyn(dev);
+    } else {
+        wsLog("WARNING: unexpected sim touch action  %d\n", action);
+    }
+}
+
diff --git a/simulator/wrapsim/DevFb.c b/simulator/wrapsim/DevFb.c
new file mode 100644
index 0000000..42a9d9a
--- /dev/null
+++ b/simulator/wrapsim/DevFb.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Fake device support.
+ */
+#include "Common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+
+typedef struct FbState {
+    /* index into gWrapSim.display[] */
+    int     displayIdx;
+
+    /* VRAM address, set by mmap() call */
+    void*   vramAddr;
+
+    /* kernel data structures */
+    struct fb_var_screeninfo    vinfo;
+    struct fb_fix_screeninfo    finfo;
+} FbState;
+
+
+/*
+ * Set up the initial values of the structs.
+ *
+ * The FbState struct is zeroed out initially, so we only need to set the
+ * fields that don't default to zero.
+ */
+static void configureInitialState(int displayIdx, FbState* fbState)
+{
+    int width, height;
+
+    assert(displayIdx >= 0 && displayIdx < gWrapSim.numDisplays);
+
+    width = gWrapSim.display[displayIdx].width;
+    height = gWrapSim.display[displayIdx].height;
+    wsLog("Configuring FbState for display %d (%dx%x key=0x%08x)\n",
+        displayIdx, width, height, gWrapSim.display[displayIdx].shmemKey);
+
+    /* fb_fix_screeninfo */
+    strcpy(fbState->finfo.id, "omapfb");
+    fbState->finfo.smem_len = (width * 2) * height * 2;
+    fbState->finfo.line_length = width * 2;
+
+    /* fb_var_screeninfo */
+    fbState->vinfo.xres = width;
+    fbState->vinfo.yres = height;
+    fbState->vinfo.xres_virtual = width;
+    fbState->vinfo.yres_virtual = height * 2;
+    fbState->vinfo.bits_per_pixel = 16;
+
+    fbState->vinfo.red.offset = 11;
+    fbState->vinfo.red.length = 5;
+    fbState->vinfo.green.offset = 5;
+    fbState->vinfo.green.length = 6;
+    fbState->vinfo.blue.offset = 0;
+    fbState->vinfo.blue.length = 5;
+
+    fbState->vinfo.width = 51;           // physical dimension, used for dpi
+    fbState->vinfo.height = 38;          
+
+    fbState->vinfo.pixclock = 103092;    
+    fbState->vinfo.upper_margin = 3;
+    fbState->vinfo.lower_margin = 227;
+    fbState->vinfo.left_margin = 12;
+    fbState->vinfo.right_margin = 8;
+}
+
+/*
+ * Free allocated state.
+ */
+static void freeState(FbState* fbState)
+{
+    free(fbState);
+}
+
+/*
+ * Wait for our synthetic vsync to happen.
+ */
+static void waitForVsync(FbState* state)
+{
+    /* TODO: simulate a real interval */
+    usleep(1000000/60);
+}
+
+/*
+ * Forward pixels to the simulator.
+ */
+static void sendPixelsToSim(FbState* state)
+{
+    if (state->vramAddr == 0) {
+        wsLog("## not sending pixels (no addr yet)\n");
+        return;
+    }
+
+    //wsLog("+++ sending pixels to sim (disp=%d yoff=%d)\n",
+    //    state->displayIdx, state->vinfo.yoffset);
+
+    wsLockDisplay(state->displayIdx);
+
+    uint8_t* dst = gWrapSim.display[state->displayIdx].addr;
+
+    int l,t,r,b,w,h;
+    w = gWrapSim.display[state->displayIdx].width;
+    h = gWrapSim.display[state->displayIdx].height;
+
+#if 0
+    /*
+     * TODO: surfaceflinger encodes the dirty region in vinfo.reserved[].  We
+     * can use that to perform a partial update.
+     */
+    const Rect dirty(dirtyReg.bounds());
+    l = dirty.left  >=0 ? dirty.left : 0;
+    t = dirty.top   >=0 ? dirty.top  : 0;
+    r = dirty.right <=w ? dirty.right  : w;
+    b = dirty.bottom<=h ? dirty.bottom : h;
+#else
+    l = t = 0;
+    r = w;
+    b = h;
+#endif
+
+    /* find the right page */
+    int ypage = state->vinfo.yoffset;
+
+    int x, y;
+    for (y = t ; y < b ; y++) {
+        // no "stride" issues with this display
+        uint8_t* outPtr = dst + (y*w+l)*3;
+        const uint16_t* ptr16 = (uint16_t*)state->vramAddr + ((y+ypage)*w+l);
+        for (x = l; x < r; x++) {
+            uint16_t in = *ptr16++;
+            uint32_t R,G,B;
+            R = ((in>>8)&0xF8) | (in>>(8+5));
+            G = (in & 0x7E0)>>3;
+            G |= G>>6;
+            B = (in & 0x1F)<<3;
+            B |= B>>5;
+            *outPtr++ = R;
+            *outPtr++ = G;
+            *outPtr++ = B;
+        }
+    }
+
+    wsUnlockDisplay(state->displayIdx);
+
+    /* notify the simulator */
+    wsPostDisplayUpdate(state->displayIdx);
+}
+
+/*
+ * Provide a memory-mapped region for framebuffer data.  We want to use a
+ * real mmap() call, not fake it with a malloc, so that related calls
+ * (munmap, madvise) will just work.
+ */
+static void* mmapFb(FakeDev* dev, void* start, size_t length, int prot,
+    int flags, int fd, __off_t offset)
+{
+    FbState* state = (FbState*) dev->state;
+    void* map;
+
+    /* be reasonable */
+    if (length > (640*480*2)*4) {
+        errno = EINVAL;
+        return MAP_FAILED;
+    }
+
+    /* this is supposed to be VRAM, so just map a chunk */
+    map = mmap(start, length, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
+
+    /* update our "VRAM address"; this feels a bit fragile */
+    if (state->vramAddr != NULL) {
+        wsLog("%s: NOTE: changing vram address from %p\n",
+            dev->debugName, state->vramAddr);
+    }
+    state->vramAddr = map;
+
+    wsLog("%s: mmap %u bytes --> %p\n", dev->debugName, length, map);
+    return map;
+}
+
+/*
+ * Handle framebuffer ioctls.
+ */
+static int ioctlFb(FakeDev* dev, int fd, int request, void* argp)
+{
+    FbState* state = (FbState*) dev->state;
+
+    wsLog("%s: ioctl(0x%x, %p)\n", dev->debugName, request, argp);
+
+    switch (request) {
+    case FBIOGET_FSCREENINFO:       // struct fb_fix_screeninfo*
+        memcpy(argp, &state->finfo, sizeof(struct fb_fix_screeninfo));
+        break;
+    case FBIOGET_VSCREENINFO:       // struct fb_var_screeninfo*
+        memcpy(argp, &state->vinfo, sizeof(struct fb_var_screeninfo));
+        break;
+    case FBIOPUT_VSCREENINFO:       // struct fb_var_screeninfo*
+        memcpy(&state->vinfo, argp, sizeof(struct fb_var_screeninfo));
+        if (state->vinfo.activate == FB_ACTIVATE_NOW) {
+            //wsLog("%s: activate now\n", dev->debugName);
+            sendPixelsToSim(state);
+        } else if (state->vinfo.activate == FB_ACTIVATE_VBL) {
+            //wsLog("%s: activate on VBL\n", dev->debugName);
+            sendPixelsToSim(state);
+            /* we wait *after* so other process gets scheduled to draw */
+            waitForVsync(state);
+        } else {
+            wsLog("%s: activate value is %d\n",
+                dev->debugName, state->vinfo.activate);
+        }
+        break;
+    case FBIOGET_VBLANK:            // struct fb_vblank*
+        /* the device doesn't actually implement this */
+        //memset(argp, 0, sizeof(struct fb_vblank));
+        errno = EINVAL;
+        return -1;
+    default:
+    /*case FBIO_WAITFORVSYNC:*/
+        wsLog("GLITCH: UNKNOWN ioctl request 0x%x on %s\n",
+            request, dev->debugName);
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Free up our state before closing down the fake descriptor.
+ */
+static int closeFb(FakeDev* dev, int fd)
+{
+    freeState((FbState*)dev->state);
+    dev->state = NULL;
+    return 0;
+}
+
+/*
+ * Open the console TTY device, which responds to a collection of ioctl()s.
+ */
+FakeDev* wsOpenDevFb(const char* pathName, int flags)
+{
+    FakeDev* newDev = wsCreateFakeDev(pathName);
+    if (newDev != NULL) {
+        newDev->mmap = mmapFb;
+        newDev->ioctl = ioctlFb;
+        newDev->close = closeFb;
+
+        FbState* fbState = calloc(1, sizeof(FbState));
+
+        /* establish a connection to the front-end if necessary */
+        /* (also gets display configuration) */
+        wsSimConnect();
+
+        configureInitialState(0, fbState);  // always use display 0 for now
+        newDev->state = fbState;
+    }
+
+    return newDev;
+}
+
diff --git a/simulator/wrapsim/DevLog.c b/simulator/wrapsim/DevLog.c
new file mode 100644
index 0000000..fe1144d
--- /dev/null
+++ b/simulator/wrapsim/DevLog.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Log devices.  We want to filter and display messages, with separate
+ * treatment for "debug" and "event" logs.
+ *
+ * All messages are just dumped to stderr.
+ */
+#include "Common.h"
+
+#include "cutils/logd.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <fcntl.h>
+
+#define kMaxTagLen  16      /* from utils/Log.cpp */
+
+#define kTagSetSize 16      /* arbitrary */
+
+/* from utils/Log.cpp */
+typedef enum {
+    FORMAT_OFF = 0,
+    FORMAT_BRIEF,
+    FORMAT_PROCESS,
+    FORMAT_TAG,
+    FORMAT_THREAD,
+    FORMAT_RAW,
+    FORMAT_TIME,
+    FORMAT_LONG
+} LogFormat;
+
+
+/*
+ * Log driver state.
+ */
+typedef struct LogState {
+    /* nonzero if this is a binary log */
+    int     isBinary;
+
+    /* global minimum priority */
+    int     globalMinPriority;
+
+    /* output format */
+    LogFormat outputFormat;
+
+    /* tags and priorities */
+    struct {
+        char    tag[kMaxTagLen];
+        int     minPriority;
+    } tagSet[kTagSetSize];
+} LogState;
+
+
+/*
+ * Configure logging based on ANDROID_LOG_TAGS environment variable.  We
+ * need to parse a string that looks like
+ *
+ *  '*:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+ *
+ * The tag (or '*' for the global level) comes first, followed by a colon
+ * and a letter indicating the minimum priority level we're expected to log.
+ * This can be used to reveal or conceal logs with specific tags.
+ *
+ * We also want to check ANDROID_PRINTF_LOG to determine how the output
+ * will look.
+ */
+static void configureInitialState(const char* pathName, LogState* logState)
+{
+    static const int kDevLogLen = 9;    /* strlen("/dev/log/") */
+
+    /* identify binary logs */
+    if (strcmp(pathName + kDevLogLen, "events") == 0) {
+        logState->isBinary = 1;
+    }
+
+    /* global min priority defaults to "info" level */
+    logState->globalMinPriority = ANDROID_LOG_INFO;
+
+    /*
+     * This is based on the utils/Log.cpp code.
+     */
+    const char* tags = getenv("ANDROID_LOG_TAGS");
+    wsLog("Found ANDROID_LOG_TAGS='%s'\n", tags);
+    if (tags != NULL) {
+        int entry = 0;
+
+        while (*tags != '\0') {
+            char tagName[kMaxTagLen];
+            int i, minPrio;
+
+            while (isspace(*tags))
+                tags++;
+
+            i = 0;
+            while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
+                i < kMaxTagLen)
+            {
+                tagName[i++] = *tags++;
+            }
+            if (i == kMaxTagLen) {
+                wsLog("ERROR: env tag too long (%d chars max)\n", kMaxTagLen-1);
+                return;
+            }
+            tagName[i] = '\0';
+
+            /* default priority, if there's no ":" part; also zero out '*' */
+            minPrio = ANDROID_LOG_VERBOSE;
+            if (tagName[0] == '*' && tagName[1] == '\0') {
+                minPrio = ANDROID_LOG_DEBUG;
+                tagName[0] = '\0';
+            }
+
+            if (*tags == ':') {
+                tags++;
+                if (*tags >= '0' && *tags <= '9') {
+                    if (*tags >= ('0' + ANDROID_LOG_SILENT))
+                        minPrio = ANDROID_LOG_VERBOSE;
+                    else
+                        minPrio = *tags - '\0';
+                } else {
+                    switch (*tags) {
+                    case 'v':   minPrio = ANDROID_LOG_VERBOSE;  break;
+                    case 'd':   minPrio = ANDROID_LOG_DEBUG;    break;
+                    case 'i':   minPrio = ANDROID_LOG_INFO;     break;
+                    case 'w':   minPrio = ANDROID_LOG_WARN;     break;
+                    case 'e':   minPrio = ANDROID_LOG_ERROR;    break;
+                    case 'f':   minPrio = ANDROID_LOG_FATAL;    break;
+                    case 's':   minPrio = ANDROID_LOG_SILENT;   break;
+                    default:    minPrio = ANDROID_LOG_DEFAULT;  break;
+                    }
+                }
+
+                tags++;
+                if (*tags != '\0' && !isspace(*tags)) {
+                    wsLog("ERROR: garbage in tag env; expected whitespace\n");
+                    wsLog("       env='%s'\n", tags);
+                    return;
+                }
+            }
+
+            if (tagName[0] == 0) {
+                logState->globalMinPriority = minPrio;
+                wsLog("+++ global min prio %d\n", logState->globalMinPriority);
+            } else {
+                logState->tagSet[entry].minPriority = minPrio;
+                strcpy(logState->tagSet[entry].tag, tagName);
+                wsLog("+++ entry %d: %s:%d\n",
+                    entry,
+                    logState->tagSet[entry].tag,
+                    logState->tagSet[entry].minPriority);
+                entry++;
+            }
+        }
+    }
+
+
+    /*
+     * Taken from utils/Log.cpp
+     */
+    const char* fstr = getenv("ANDROID_PRINTF_LOG");
+    LogFormat format;
+    if (fstr == NULL) {
+        format = FORMAT_BRIEF;
+    } else {
+        if (strcmp(fstr, "brief") == 0)
+            format = FORMAT_BRIEF;
+        else if (strcmp(fstr, "process") == 0)
+            format = FORMAT_PROCESS;
+        else if (strcmp(fstr, "tag") == 0)
+            format = FORMAT_PROCESS;
+        else if (strcmp(fstr, "thread") == 0)
+            format = FORMAT_PROCESS;
+        else if (strcmp(fstr, "raw") == 0)
+            format = FORMAT_PROCESS;
+        else if (strcmp(fstr, "time") == 0)
+            format = FORMAT_PROCESS;
+        else if (strcmp(fstr, "long") == 0)
+            format = FORMAT_PROCESS;
+        else
+            format = (LogFormat) atoi(fstr);        // really?!
+    }
+
+    logState->outputFormat = format;
+}
+
+/*
+ * Free up the state structure.
+ */
+static void freeState(LogState* logState)
+{
+    free(logState);
+}
+
+/*
+ * Return a human-readable string for the priority level.  Always returns
+ * a valid string.
+ */
+static const char* getPriorityString(int priority)
+{
+    /* the first character of each string should be unique */
+    static const char* priorityStrings[] = {
+        "Verbose", "Debug", "Info", "Warn", "Error", "Assert"
+    };
+    int idx;
+
+    idx = (int) priority - (int) ANDROID_LOG_VERBOSE;
+    if (idx < 0 ||
+        idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0])))
+        return "?unknown?";
+    return priorityStrings[idx];
+}
+
+/*
+ * Show a log message.  We write it to stderr and send a copy to the
+ * simulator front-end for the log window.
+ *
+ * Taken from utils/Log.cpp.
+ */
+static void showLog(FakeDev* dev, int logPrio, const char* tag, const char* msg)
+{
+    LogState* state = (LogState*) dev->state;
+
+#if defined(HAVE_LOCALTIME_R)
+    struct tm tmBuf;
+#endif
+    struct tm* ptm;
+    char timeBuf[32];
+    char prefixBuf[128], suffixBuf[128];
+    char priChar;
+    time_t when;
+    pid_t pid, tid;
+
+    //wsLog("LOG %d: %s %s", logPrio, tag, msg);
+    wsPostLogMessage(logPrio, tag, msg);
+
+    priChar = getPriorityString(logPrio)[0];
+    when = time(NULL);
+    pid = tid = getpid();       // find gettid()?
+
+    /*
+     * Get the current date/time in pretty form
+     *
+     * It's often useful when examining a log with "less" to jump to
+     * a specific point in the file by searching for the date/time stamp.
+     * For this reason it's very annoying to have regexp meta characters
+     * in the time stamp.  Don't use forward slashes, parenthesis,
+     * brackets, asterisks, or other special chars here.
+     */
+#if defined(HAVE_LOCALTIME_R)
+    ptm = localtime_r(&when, &tmBuf);
+#else
+    ptm = localtime(&when);
+#endif
+    //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
+    strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+    /*
+     * Construct a buffer containing the log header and log message.
+     */
+    size_t prefixLen, suffixLen;
+
+    switch (state->outputFormat) {
+    case FORMAT_TAG:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "%c/%-8s: ", priChar, tag);
+        strcpy(suffixBuf, "\n"); suffixLen = 1;
+        break;
+    case FORMAT_PROCESS:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "%c(%5d) ", priChar, pid);
+        suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
+            "  (%s)\n", tag);
+        break;
+    case FORMAT_THREAD:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "%c(%5d:%p) ", priChar, pid, (void*)tid);
+        strcpy(suffixBuf, "\n"); suffixLen = 1;
+        break;
+    case FORMAT_RAW:
+        prefixBuf[0] = 0; prefixLen = 0;
+        strcpy(suffixBuf, "\n"); suffixLen = 1;
+        break;
+    case FORMAT_TIME:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "%s %-8s\n\t", timeBuf, tag);
+        strcpy(suffixBuf, "\n"); suffixLen = 1;
+        break;
+    case FORMAT_LONG:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "[ %s %5d:%p %c/%-8s ]\n",
+            timeBuf, pid, (void*)tid, priChar, tag);
+        strcpy(suffixBuf, "\n\n"); suffixLen = 2;
+        break;
+    default:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "%c/%-8s(%5d): ", priChar, tag, pid);
+        strcpy(suffixBuf, "\n"); suffixLen = 1;
+        break;
+     }
+
+    /*
+     * Figure out how many lines there will be.
+     */
+    const char* end = msg + strlen(msg);
+    size_t numLines = 0;
+    const char* p = msg;
+    while (p < end) {
+        if (*p++ == '\n') numLines++;
+    }
+    if (p > msg && *(p-1) != '\n') numLines++;
+    
+    /*
+     * Create an array of iovecs large enough to write all of
+     * the lines with a prefix and a suffix.
+     */
+    const size_t INLINE_VECS = 6;
+    struct iovec stackVec[INLINE_VECS];
+    struct iovec* vec = stackVec;
+    
+    numLines *= 3;  // 3 iovecs per line.
+    if (numLines > INLINE_VECS) {
+        vec = (struct iovec*)malloc(sizeof(struct iovec)*numLines);
+        if (vec == NULL) {
+            msg = "LOG: write failed, no memory";
+            numLines = 3;
+        }
+    }
+    
+    /*
+     * Fill in the iovec pointers.
+     */
+    p = msg;
+    struct iovec* v = vec;
+    int totalLen = 0;
+    while (p < end) {
+        if (prefixLen > 0) {
+            v->iov_base = prefixBuf;
+            v->iov_len = prefixLen;
+            totalLen += prefixLen;
+            v++;
+        }
+        const char* start = p;
+        while (p < end && *p != '\n') p++;
+        if ((p-start) > 0) {
+            v->iov_base = (void*)start;
+            v->iov_len = p-start;
+            totalLen += p-start;
+            v++;
+        }
+        if (*p == '\n') p++;
+        if (suffixLen > 0) {
+            v->iov_base = suffixBuf;
+            v->iov_len = suffixLen;
+            totalLen += suffixLen;
+            v++;
+        }
+    }
+    
+    /*
+     * Write the entire message to the log file with a single writev() call.
+     * We need to use this rather than a collection of printf()s on a FILE*
+     * because of multi-threading and multi-process issues.
+     *
+     * If the file was not opened with O_APPEND, this will produce interleaved
+     * output when called on the same file from multiple processes.
+     *
+     * If the file descriptor is actually a network socket, the writev()
+     * call may return with a partial write.  Putting the writev() call in
+     * a loop can result in interleaved data.  This can be alleviated
+     * somewhat by wrapping the writev call in the Mutex.
+     */
+
+    for(;;) {
+        int cc;
+
+        cc = writev(fileno(stderr), vec, v-vec);
+        if (cc == totalLen) break;
+        
+        if (cc < 0) {
+            if(errno == EINTR) continue;
+            
+                /* can't really log the failure; for now, throw out a stderr */
+            fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
+            break;
+        } else {
+                /* shouldn't happen when writing to file or tty */
+            fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen);
+            break;
+        }
+    }
+
+    /* if we allocated storage for the iovecs, free it */
+    if (vec != stackVec)
+        free(vec);
+}
+
+
+/*
+ * Receive a log message.  We happen to know that "vector" has three parts:
+ *
+ *  priority (1 byte)
+ *  tag (N bytes -- null-terminated ASCII string)
+ *  message (N bytes -- null-terminated ASCII string)
+ */
+static ssize_t writevLog(FakeDev* dev, int fd, const struct iovec* vector,
+    int count)
+{
+    LogState* state = (LogState*) dev->state;
+    int ret = 0;
+
+    if (state->isBinary) {
+        wsLog("%s: ignoring binary log\n", dev->debugName);
+        goto bail;
+    }
+
+    if (count != 3) {
+        wsLog("%s: writevLog with count=%d not expected\n",
+            dev->debugName, count);
+        errno = EINVAL;
+        return -1;
+    }
+
+    /* pull out the three fields */
+    int logPrio = *(const char*)vector[0].iov_base;
+    const char* tag = (const char*) vector[1].iov_base;
+    const char* msg = (const char*) vector[2].iov_base;
+
+    /* see if this log tag is configured */
+    int i;
+    int minPrio = state->globalMinPriority;
+    for (i = 0; i < kTagSetSize; i++) {
+        if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
+            break;      /* reached end of configured values */
+
+        if (strcmp(state->tagSet[i].tag, tag) == 0) {
+            //wsLog("MATCH tag '%s'\n", tag);
+            minPrio = state->tagSet[i].minPriority;
+            break;
+        }
+    }
+
+    if (logPrio >= minPrio) {
+        showLog(dev, logPrio, tag, msg);
+    } else {
+        //wsLog("+++ NOLOG(%d): %s %s", logPrio, tag, msg);
+    }
+
+bail:
+    for (i = 0; i < count; i++)
+        ret += vector[i].iov_len;
+    return ret;
+}
+
+/*
+ * Free up our state before closing down the fake descriptor.
+ */
+static int closeLog(FakeDev* dev, int fd)
+{
+    freeState((LogState*)dev->state);
+    dev->state = NULL;
+    return 0;
+}
+
+/*
+ * Open a log output device.
+ */
+FakeDev* wsOpenDevLog(const char* pathName, int flags)
+{
+    FakeDev* newDev = wsCreateFakeDev(pathName);
+    if (newDev != NULL) {
+        newDev->writev = writevLog;
+        newDev->close = closeLog;
+
+        LogState* logState = calloc(1, sizeof(LogState));
+
+        configureInitialState(pathName, logState);
+        newDev->state = logState;
+    }
+
+    return newDev;
+}
+
diff --git a/simulator/wrapsim/DevPower.c b/simulator/wrapsim/DevPower.c
new file mode 100644
index 0000000..2d25704
--- /dev/null
+++ b/simulator/wrapsim/DevPower.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Magic entries in /sys/android_power/.
+ */
+#include "Common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#if 0
+/*
+ * Set of entries found in /sys/android_power.
+ */
+typedef enum DeviceIndex {
+    kPowerUnknown = 0,
+
+    kPowerAutoOffTimeout,
+    kPowerBatteryLevel,
+    kPowerBatteryLevelLow,
+    kPowerBatteryLevelRaw,
+    kPowerBatteryLevelScale,
+    kPowerBatteryLowLevel,
+    kPowerBatteryShutdownLevel,
+    kPowerChargingState,
+    kPowerRequestState,
+    kPowerState,
+
+    kPowerAcquireFullWakeLock,
+    kPowerAcquirePartialWakeLock,
+    kPowerReleaseWakeLock,
+} DeviceIndex;
+#endif
+
+/*
+ * Map filename to device index.
+ *
+ * [ not using DeviceIndex -- would be useful if we need to return something
+ * other than a static string ]
+ */
+static const struct {
+    const char*     name;
+    //DeviceIndex     idx;
+    const char*     data;
+} gDeviceMap[] = {
+    { "auto_off_timeout",           //kPowerAutoOffTimeout,
+        "\n" },
+    { "battery_level",              //kPowerBatteryLevel,
+        "9\n" },
+    { "battery_level_low",          //kPowerBatteryLevelLow,
+        "0\n" },
+    { "battery_level_raw",          //kPowerBatteryLevelRaw,
+        "100\n" },
+    { "battery_level_scale",        //kPowerBatteryLevelScale,
+        "9\n" },
+    { "battery_low_level",          //kPowerBatteryLowLevel,
+        "10\n" },
+    { "battery_shutdown_level",     //kPowerBatteryShutdownLevel,
+        "5\n", },
+    { "charging_state",             //kPowerChargingState,
+        "Maintaining\n" },
+    { "request_state",              //kPowerRequestState,
+        "wake\n" },
+    { "state",                      //kPowerState,
+        "0-1-0\n" },
+
+    { "acquire_full_wake_lock",     //kPowerAcquireFullWakeLock,
+        "\n" },
+    { "acquire_partial_wake_lock",  //kPowerAcquirePartialWakeLock,
+        "\n" },
+    { "release_wake_lock",          //kPowerReleaseWakeLock,
+        "radio-interface PowerManagerService KeyEvents\n" },
+    { "wait_for_fb_sleep",          //kSleepFileName,
+        "" },                       // this means "block forever on read"
+    { "wait_for_fb_wake",           //kWakeFileName,
+        "0" },
+
+};
+
+/*
+ * Power driver state.
+ *
+ * Right now we just ignore everything written.
+ */
+typedef struct PowerState {
+    int         which;
+} PowerState;
+
+
+/*
+ * Figure out who we are, based on "pathName".
+ */
+static void configureInitialState(const char* pathName, PowerState* powerState)
+{
+    const char* cp = pathName + strlen("/sys/android_power/");
+    int i;
+
+    powerState->which = -1;
+    for (i = 0; i < (int) (sizeof(gDeviceMap) / sizeof(gDeviceMap[0])); i++) {
+        if (strcmp(cp, gDeviceMap[i].name) == 0) {
+            powerState->which = i;
+            break;
+        }
+    }
+
+    if (powerState->which == -1) {
+        wsLog("Warning: access to unknown power device '%s'\n", pathName);
+        return;
+    }
+}
+
+/*
+ * Free up the state structure.
+ */
+static void freeState(PowerState* powerState)
+{
+    free(powerState);
+}
+
+/*
+ * Read data from the device.
+ *
+ * We don't try to keep track of how much was read -- existing clients just
+ * try to read into a large buffer.
+ */
+static ssize_t readPower(FakeDev* dev, int fd, void* buf, size_t count)
+{
+    PowerState* state = (PowerState*) dev->state;
+    int dataLen;
+
+    wsLog("%s: read %d\n", dev->debugName, count);
+
+    if (state->which < 0 || state->which >= sizeof(gDeviceMap)/sizeof(gDeviceMap[0]))
+        return 0;
+
+    const char* data = gDeviceMap[state->which].data;
+    size_t strLen = strlen(data);
+
+    while(strLen == 0)
+        sleep(10); // block forever
+
+    ssize_t copyCount = (strLen < count) ? strLen : count;
+    memcpy(buf, data, copyCount);
+    return copyCount;
+}
+
+/*
+ * Ignore the request.
+ */
+static ssize_t writePower(FakeDev* dev, int fd, const void* buf, size_t count)
+{
+    wsLog("%s: write %d bytes\n", dev->debugName, count);
+    return count;
+}
+
+/*
+ * Our Java classes want to be able to do ioctl(FIONREAD) on files.  The
+ * battery power manager is blowing up if we get an error other than
+ * ENOTTY (meaning a device that doesn't understand buffering).
+ */
+static int ioctlPower(FakeDev* dev, int fd, int request, void* argp)
+{
+    if (request == FIONREAD) {
+        wsLog("%s: ioctl(FIONREAD, %p)\n", dev->debugName, argp);
+        errno = ENOTTY;
+        return -1;
+    } else {
+        wsLog("%s: ioctl(0x%08x, %p) ??\n", dev->debugName, request, argp);
+        errno = EINVAL;
+        return -1;
+    }
+}
+
+/*
+ * Free up our state before closing down the fake descriptor.
+ */
+static int closePower(FakeDev* dev, int fd)
+{
+    freeState((PowerState*)dev->state);
+    dev->state = NULL;
+    return 0;
+}
+
+/*
+ * Open a power device.
+ */
+FakeDev* wsOpenDevPower(const char* pathName, int flags)
+{
+    FakeDev* newDev = wsCreateFakeDev(pathName);
+    if (newDev != NULL) {
+        newDev->read = readPower;
+        newDev->write = writePower;
+        newDev->ioctl = ioctlPower;
+        newDev->close = closePower;
+
+        PowerState* powerState = calloc(1, sizeof(PowerState));
+
+        configureInitialState(pathName, powerState);
+        newDev->state = powerState;
+    }
+
+    return newDev;
+}
+
diff --git a/simulator/wrapsim/DevVibrator.c b/simulator/wrapsim/DevVibrator.c
new file mode 100644
index 0000000..b736f75
--- /dev/null
+++ b/simulator/wrapsim/DevVibrator.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Vibrating notification device.
+ */
+#include "Common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <unistd.h>
+
+
+/*
+ * The user will write a decimal integer indicating the time, in milliseconds,
+ * that the device should vibrate.  In current usage, this is either -1
+ * (meaning vibrate forever) or 0 (don't vibrate).
+ */
+static ssize_t writeVibrator(FakeDev* dev, int fd, const void* buf,
+    size_t count)
+{
+    if (count == 2 && memcmp(buf, "0\n", 2) == 0) {
+        wsEnableVibration(0);
+    } else if (count == 3 && memcmp(buf, "-1\n", 3) == 0) {
+        wsEnableVibration(1);
+    } else {
+        wsLog("%s: got %d bytes: '%*s'\n",
+            dev->debugName, count, count, (const char*) buf);
+    }
+
+    return count;
+}
+
+/*
+ * Open the vibration control device.
+ */
+FakeDev* wsOpenDevVibrator(const char* pathName, int flags)
+{
+    FakeDev* newDev = wsCreateFakeDev(pathName);
+    if (newDev != NULL) {
+        newDev->write = writeVibrator;
+    }
+
+    return newDev;
+}
+
diff --git a/simulator/wrapsim/FakeDev.c b/simulator/wrapsim/FakeDev.c
new file mode 100644
index 0000000..75aee16
--- /dev/null
+++ b/simulator/wrapsim/FakeDev.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Fake device support.
+ */
+/*
+Implementation notes:
+
+There are a couple of basic scenarios, exemplified by the "fb" and
+"events" devices.  The framebuffer driver is pretty simple, handling a
+few ioctl()s and managing a stretch of memory.  We can just intercept a
+few calls.  The input event driver can be used in a select() or poll()
+call with other file descriptors, which either requires us to do some
+fancy tricks with select() and poll(), or requires that we return a real
+file descriptor (perhaps based on a socketpair).
+
+We have three basic approaches to dealing with "fake" file descriptors:
+
+(1) Always use real fds.  We can dup() an open /dev/null to get a number
+    for the cases where we don't need a socketpair.
+(2) Always use fake fds with absurdly high numeric values.  Testing to see
+    if the fd is one we handle is trivial (range check).  This doesn't
+    work for select(), which uses fd bitmaps accessed through macros.
+(3) Use a mix of real and fake fds, in a high range (512-1023).  Because
+    it's in the "real" range, we can pass real fds around for things that
+    are handed to poll() and select(), but because of the high numeric
+    value we *should* be able to get away with a trivial range check.
+
+Approach (1) is the most portable and least likely to break, but the
+efficiencies gained in approach (2) make it more desirable.  There is
+a small risk of application fds wandering into our range, but we can
+minimize that by asserting on a "guard zone" and/or obstructing dup2().
+(We can also dup2(/dev/null) to "reserve" our fds, but that wastes
+resources.)
+*/
+
+#include "Common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <assert.h>
+#include <fnmatch.h>
+
+/*
+ * Devices we intercept.
+ *
+ * Needed:
+ *  /sys/android_power/battery_level_scale
+ *  /sys/android_power/battery_level
+ *  /sys/android_power/battery_level_raw
+ *  /sys/android_power/charging_state
+ *  /dev/alarm
+ *  radio
+ */
+typedef FakeDev* (*wsFileHook)(const char *path, int flags);
+
+typedef struct FakedPath {
+    const char *pathexpr;
+    wsFileHook hook;
+} FakedPath;
+
+FakedPath fakedpaths[] =
+{
+    { "/dev/graphics/fb0",      wsOpenDevFb },
+    { "/dev/hw3d",              NULL },
+    { "/dev/eac",               wsOpenDevAudio },
+    { "/dev/tty0",              wsOpenDevConsoleTty },
+    { "/dev/input/event0",      wsOpenDevEvent },
+    { "/dev/input/*",           NULL },
+    { "/dev/log/*",             wsOpenDevLog },
+    { "/sys/android_power/*",   wsOpenDevPower },
+    { "/sys/devices/platform/android-vibrator/enable",  wsOpenDevVibrator },
+    { "/sys/qemu_trace/*",      NULL },
+    { NULL,                     NULL }
+};
+
+
+/*
+ * Generic drop-in for an unimplemented call.
+ *
+ * Returns -1, which conveniently is the same as MAP_FAILED for mmap.
+ */
+static int notImplemented(FakeDev* dev, const char* callName)
+{
+    wsLog("WARNING: unimplemented %s() on '%s' %p\n",
+        callName, dev->debugName, dev->state);
+    errno = kNoHandlerError;
+    return -1;
+}
+
+/*
+ * Default implementations.  We want to log as much information as we can
+ * so that we can fill in the missing implementation.
+ *
+ * TODO: for some or all of these we will want to display the full arg list.
+ */
+static int noClose(FakeDev* dev, ...)
+{
+    return 0;
+}
+static int noRead(FakeDev* dev, ...)
+{
+    return notImplemented(dev, "read");
+}
+static int noReadv(FakeDev* dev, ...)
+{
+    return notImplemented(dev, "readv");
+}
+static int noWrite(FakeDev* dev, ...)
+{
+    return notImplemented(dev, "write");
+}
+static int noWritev(FakeDev* dev, ...)
+{
+    return notImplemented(dev, "writev");
+}
+static int noMmap(FakeDev* dev, ...)
+{
+    return notImplemented(dev, "mmap");
+}
+static int noIoctl(FakeDev* dev, ...)
+{
+    return notImplemented(dev, "ioctl");
+}
+
+
+/*
+ * Create a new FakeDev entry.
+ *
+ * We mark the fd slot as "used" in the bitmap, but don't add it to the
+ * table yet since the entry is not fully prepared.
+ */
+FakeDev* wsCreateFakeDev(const char* debugName)
+{
+    FakeDev* newDev;
+    int cc;
+
+    assert(debugName != NULL);
+
+    newDev = (FakeDev*) calloc(1, sizeof(FakeDev));
+    if (newDev == NULL)
+        return NULL;
+
+    newDev->debugName = strdup(debugName);
+    newDev->state = NULL;
+
+    newDev->close = (Fake_close) noClose;
+    newDev->read = (Fake_read) noRead;
+    newDev->readv = (Fake_readv) noReadv;
+    newDev->write = (Fake_write) noWrite;
+    newDev->writev = (Fake_writev) noWritev;
+    newDev->mmap = (Fake_mmap) noMmap;
+    newDev->ioctl = (Fake_ioctl) noIoctl;
+
+    /*
+     * Allocate a new entry.  The bit vector map is really only used as a
+     * performance boost in the current implementation.
+     */
+    cc = pthread_mutex_lock(&gWrapSim.fakeFdLock); assert(cc == 0);
+    int newfd = wsAllocBit(gWrapSim.fakeFdMap);
+    cc = pthread_mutex_unlock(&gWrapSim.fakeFdLock); assert(cc == 0);
+
+    if (newfd < 0) {
+        wsLog("WARNING: ran out of 'fake' file descriptors\n");
+        free(newDev);
+        return NULL;
+    }
+    newDev->fd = newfd + kFakeFdBase;
+    newDev->otherFd = -1;
+    assert(gWrapSim.fakeFdList[newDev->fd - kFakeFdBase] == NULL);
+
+    return newDev;
+}
+
+/*
+ * Create a new FakeDev entry, and open a file descriptor that actually
+ * works.
+ */
+FakeDev* wsCreateRealFakeDev(const char* debugName)
+{
+    FakeDev* newDev = wsCreateFakeDev(debugName);
+    if (newDev == NULL)
+        return newDev;
+    
+    int fds[2];
+
+    if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+        wsLog("socketpair() failed: %s\n", strerror(errno));
+        wsFreeFakeDev(newDev);
+        return NULL;
+    }
+
+    if (dup2(fds[0], newDev->fd) < 0) {
+        wsLog("dup2(%d,%d) failed: %s\n",
+            fds[0], newDev->fd, strerror(errno));
+        wsFreeFakeDev(newDev);
+        return NULL;
+    }
+    close(fds[0]);
+
+    /* okay to leave this one in the "normal" range; not visible to app */
+    newDev->otherFd = fds[1];
+
+    return newDev;
+}
+
+/*
+ * Free fake device entry.
+ */
+void wsFreeFakeDev(FakeDev* dev)
+{
+    if (dev == NULL)
+        return;
+
+    wsLog("## closing/freeing '%s' (%d/%d)\n",
+        dev->debugName, dev->fd, dev->otherFd);
+
+    /*
+     * If we assigned a file descriptor slot, free it up.
+     */
+    if (dev->fd >= 0) {
+        int cc;
+
+        gWrapSim.fakeFdList[dev->fd - kFakeFdBase] = NULL;
+
+        cc = pthread_mutex_lock(&gWrapSim.fakeFdLock); assert(cc == 0);
+        wsFreeBit(gWrapSim.fakeFdMap, dev->fd - kFakeFdBase);
+        cc = pthread_mutex_unlock(&gWrapSim.fakeFdLock); assert(cc == 0);
+    }
+    if (dev->otherFd >= 0)
+        close(dev->otherFd);
+
+    if (dev->debugName) free(dev->debugName);
+    free(dev);
+}
+
+/*
+ * Map a file descriptor to a fake device.
+ *
+ * Returns NULL if there's no corresponding entry.
+ */
+FakeDev* wsFakeDevFromFd(int fd)
+{
+    /* quick range test */
+    if (fd < kFakeFdBase || fd >= kFakeFdBase + kMaxFakeFdCount)
+        return NULL;
+
+    return gWrapSim.fakeFdList[fd - kFakeFdBase];
+}
+
+
+/*
+ * Check to see if we're opening a device that we want to fake out.
+ *
+ * We return a file descriptor >= 0 on success, -1 if we're not interested,
+ * or -2 if we explicitly want to pretend that the device doesn't exist.
+ */
+int wsInterceptDeviceOpen(const char* pathName, int flags)
+{
+    FakedPath* p = fakedpaths;
+
+    while (p->pathexpr) {
+        if (fnmatch(p->pathexpr, pathName, 0) == 0) {
+            if (p->hook != NULL) {
+                FakeDev* dev = p->hook(pathName, flags);
+                if (dev != NULL) {
+                    /*
+                     * Now that the device entry is ready, add it to the list.
+                     */
+                    wsLog("## created fake dev %d: '%s' %p\n",
+                        dev->fd, dev->debugName, dev->state);
+                    gWrapSim.fakeFdList[dev->fd - kFakeFdBase] = dev;
+                    return dev->fd;
+                }
+            } else {
+                wsLog("## rejecting attempt to open %s\n", pathName);
+                errno = ENOENT;
+                return -2;
+            }
+            break;
+        }
+        p++;
+    }
+    return -1;
+}
+
+/*
+ * Check to see if we're accessing a device that we want to fake out.
+ * Returns 0 if the device can be (fake) opened with the given mode,
+ * -1 if it can't, -2 if it can't and we don't want to allow fallback
+ * to the host-device either.
+ * TODO: actually check the mode.
+ */
+int wsInterceptDeviceAccess(const char *pathName, int mode)
+{
+    FakedPath *p = fakedpaths;
+
+    while (p->pathexpr) {
+        if (fnmatch(p->pathexpr, pathName, 0) == 0) {
+            if (p->hook) {
+                return 0;
+            } else {
+                wsLog("## rejecting attempt to open %s\n", pathName);
+                errno = ENOENT;
+                return -2;
+            }
+            break;
+        }
+        p++;
+    }
+    errno = ENOENT;
+    return -1;
+}
diff --git a/simulator/wrapsim/FakeDev.h b/simulator/wrapsim/FakeDev.h
new file mode 100644
index 0000000..eacbbf5
--- /dev/null
+++ b/simulator/wrapsim/FakeDev.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Fake device support.
+ */
+#ifndef _WRAPSIM_FAKEDEV_H
+#define _WRAPSIM_FAKEDEV_H
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+typedef struct FakeDev FakeDev;
+
+typedef int     (*Fake_close)(FakeDev* dev, int);
+typedef ssize_t (*Fake_read)(FakeDev* dev, int, void*, size_t);
+typedef ssize_t (*Fake_readv)(FakeDev* dev, int, const struct iovec*, int);
+typedef ssize_t (*Fake_write)(FakeDev* dev, int, const void*, size_t);
+typedef ssize_t (*Fake_writev)(FakeDev* dev, int, const struct iovec*, int);
+typedef void*   (*Fake_mmap)(FakeDev* dev, void*, size_t, int, int, int, __off_t);
+typedef int     (*Fake_ioctl)(FakeDev* dev, int, int, void*);
+
+/*
+ * An open fake device entry.
+ */
+struct FakeDev {
+    /* string, for debugging; usually orig. device name */
+    char*   debugName;
+
+    /* state bucket */
+    void*   state;
+
+    /* the file descriptor we're associated with */
+    int     fd;
+
+    /* in some cases we use a pair; this is the other one */
+    int     otherFd;
+
+    /*
+     * Device functions we provide.
+     *
+     * All other file descriptor operations should fail, usually with EBADF.
+     */
+    Fake_close  close;
+    Fake_read   read;
+    Fake_readv  readv;
+    Fake_write  write;
+    Fake_writev writev;
+    Fake_mmap   mmap;       // handles both mmap() and mmap64()
+    Fake_ioctl  ioctl;
+};
+
+/*
+ * If a handler isn't defined for a syscall, we return EMLINK so that it's
+ * obvious when the error is generated by us.
+ */ 
+#define kNoHandlerError EMLINK
+
+/*
+ * Fake file descriptors begin here.  This should be chosen such that no
+ * real descriptor is ever at or above this value.
+ */
+#define kFakeFdBase     512
+#define kMaxFakeFdCount 256
+
+/*
+ * Create a new, completely fake device entry.
+ */
+FakeDev* wsCreateFakeDev(const char* debugName);
+
+/*
+ * Create a new, mostly fake device entry.
+ */
+FakeDev* wsCreateRealFakeDev(const char* debugName);
+
+/*
+ * Free a fake device entry.
+ */
+void wsFreeFakeDev(FakeDev* dev);
+
+/*
+ * Given a file descriptor, find the corresponding fake device.  Returns
+ * NULL if the fd doesn't correspond to one of our entries.
+ */
+FakeDev* wsFakeDevFromFd(int fd);
+
+/*
+ * Check to see if this open() call is on a device we want to spoof.
+ *
+ * Returns a "fake" file descriptor on success, <0 on error.
+ */
+int wsInterceptDeviceOpen(const char* pathName, int flags);
+
+/*
+ * Check to see if this access() call is on a device we want to spoof.
+ *
+ * Returns 0 if the device can be fake-accessed, -1 if it can't, -2
+ * if it can't and we don't want to allow fallback to the host-device.
+ */
+int wsInterceptDeviceAccess(const char* pathName, int flags);
+
+/*
+ * Devices.
+ */
+FakeDev* wsOpenDevAudio(const char* pathName, int flags);
+FakeDev* wsOpenDevConsoleTty(const char* pathName, int flags);
+FakeDev* wsOpenDevEvent(const char* pathName, int flags);
+FakeDev* wsOpenDevFb(const char* pathName, int flags);
+FakeDev* wsOpenDevLog(const char* pathName, int flags);
+FakeDev* wsOpenDevPower(const char* pathName, int flags);
+FakeDev* wsOpenDevVibrator(const char* pathName, int flags);
+
+/*
+ * Performs key remapping and sends the event to the global input event device.
+ */
+void wsSendSimKeyEvent(int key, int isDown);
+
+/*
+ * Send a touch event to the global input event device.
+ */
+void wsSendSimTouchEvent(int action, int x, int y);
+
+#endif /*_WRAPSIM_FAKEDEV_H*/
diff --git a/simulator/wrapsim/Globals.h b/simulator/wrapsim/Globals.h
new file mode 100644
index 0000000..75c98d8
--- /dev/null
+++ b/simulator/wrapsim/Globals.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Sim wrapper global state.
+ */
+#ifndef _WRAPSIM_GLOBALS_H
+#define _WRAPSIM_GLOBALS_H
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <utime.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <pthread.h>
+
+/*
+ * Type declarations for the functions we're replacing.
+ *
+ * For syscalls this matches the syscall definition, not the libc definition,
+ * e.g. no varargs for open() or ioctl().
+ */
+typedef int     (*Func_access)(const char*, int);
+typedef int     (*Func_open)(const char*, int, mode_t);
+typedef int     (*Func_open64)(const char*, int, mode_t);
+
+typedef int     (*Func_close)(int);
+typedef ssize_t (*Func_read)(int, void*, size_t);
+typedef ssize_t (*Func_readv)(int, const struct iovec*, int);
+typedef ssize_t (*Func_write)(int, const void*, size_t);
+typedef ssize_t (*Func_writev)(int, const struct iovec*, int);
+typedef void*   (*Func_mmap)(void*, size_t, int, int, int, __off_t);
+typedef void*   (*Func_mmap64)(void*, size_t, int, int, int, __off64_t);
+typedef int     (*Func_ioctl)(int, int, void*);
+
+typedef int     (*Func_chdir)(const char*);
+typedef int     (*Func_chmod)(const char*, mode_t);
+typedef int     (*Func_chown)(const char*, uid_t, uid_t);
+typedef int     (*Func_creat)(const char*, mode_t);
+typedef int     (*Func_execve)(const char*, char* const[], char* const[]);
+typedef char*   (*Func_getcwd)(char* buf, size_t size);
+typedef int     (*Func_lchown)(const char*, uid_t, uid_t);
+typedef int     (*Func_link)(const char*, const char*);
+typedef int     (*Func_lstat)(const char*, struct stat*);
+typedef int     (*Func_lstat64)(const char*, struct stat*);
+typedef int     (*Func___lxstat)(int version, const char*, struct stat*);
+typedef int     (*Func___lxstat64)(int version, const char*, struct stat*);
+typedef int     (*Func_mkdir)(const char*, mode_t mode);
+typedef ssize_t (*Func_readlink)(const char*, char*, size_t);
+typedef int     (*Func_rename)(const char*, const char*);
+typedef int     (*Func_rmdir)(const char*);
+typedef int     (*Func_stat)(const char*, struct stat*);
+typedef int     (*Func_stat64)(const char*, struct stat*);
+typedef int     (*Func___xstat)(int version, const char*, struct stat*);
+typedef int     (*Func___xstat64)(int version, const char*, struct stat*);
+typedef int     (*Func_statfs)(const char*, struct statfs*);
+typedef int     (*Func_statfs64)(const char*, struct statfs*);
+typedef int     (*Func_symlink)(const char*, const char*);
+typedef int     (*Func_unlink)(const char*);
+typedef int     (*Func_utime)(const char*, const struct utimbuf*);
+typedef int     (*Func_utimes)(const char*, const struct timeval []);
+
+typedef int     (*Func_execl)(const char*, const char*, ...);
+typedef int     (*Func_execle)(const char*, const char*, ...);
+typedef int     (*Func_execlp)(const char*, const char*, ...);
+typedef int     (*Func_execv)(const char*, char* const []);
+typedef int     (*Func_execvp)(const char*, char* const []);
+typedef FILE*   (*Func_fopen)(const char*, const char*);
+typedef FILE*   (*Func_fopen64)(const char*, const char*);
+typedef FILE*   (*Func_freopen)(const char*, const char*, FILE*);
+typedef int     (*Func_ftw)(const char*,
+                    int (*fn) (const char*, const struct stat*, int),
+                    int);
+typedef DIR*    (*Func_opendir)(const char* path);
+typedef void*   (*Func_dlopen)(const char*, int);
+
+typedef int     (*Func_setpriority)(int, int, int);
+//typedef int     (*Func_pipe)(int [2]);
+
+
+/*
+ * Pointers to the actual implementations.
+ */
+#ifndef CREATE_FUNC_STORAGE
+# define EXTERN_FUNC extern
+#else
+# define EXTERN_FUNC
+#endif
+EXTERN_FUNC Func_access _ws_access;
+EXTERN_FUNC Func_open _ws_open;
+EXTERN_FUNC Func_open64 _ws_open64;
+
+EXTERN_FUNC Func_close _ws_close;
+EXTERN_FUNC Func_read _ws_read;
+EXTERN_FUNC Func_readv _ws_readv;
+EXTERN_FUNC Func_write _ws_write;
+EXTERN_FUNC Func_writev _ws_writev;
+EXTERN_FUNC Func_mmap _ws_mmap;
+EXTERN_FUNC Func_mmap64 _ws_mmap64;
+EXTERN_FUNC Func_ioctl _ws_ioctl;
+
+EXTERN_FUNC Func_chdir _ws_chdir;
+EXTERN_FUNC Func_chmod _ws_chmod;
+EXTERN_FUNC Func_chown _ws_chown;
+EXTERN_FUNC Func_creat _ws_creat;
+EXTERN_FUNC Func_execve _ws_execve;
+EXTERN_FUNC Func_getcwd _ws_getcwd;
+EXTERN_FUNC Func_lchown _ws_lchown;
+EXTERN_FUNC Func_link _ws_link;
+EXTERN_FUNC Func_lstat _ws_lstat;
+EXTERN_FUNC Func_lstat64 _ws_lstat64;
+EXTERN_FUNC Func___lxstat _ws___lxstat;
+EXTERN_FUNC Func___lxstat64 _ws___lxstat64;
+EXTERN_FUNC Func_mkdir _ws_mkdir;
+EXTERN_FUNC Func_readlink _ws_readlink;
+EXTERN_FUNC Func_rename _ws_rename;
+EXTERN_FUNC Func_rmdir _ws_rmdir;
+EXTERN_FUNC Func_stat _ws_stat;
+EXTERN_FUNC Func_stat64 _ws_stat64;
+EXTERN_FUNC Func___xstat _ws___xstat;
+EXTERN_FUNC Func___xstat64 _ws___xstat64;
+EXTERN_FUNC Func_statfs _ws_statfs;
+EXTERN_FUNC Func_statfs64 _ws_statfs64;
+EXTERN_FUNC Func_symlink _ws_symlink;
+EXTERN_FUNC Func_unlink _ws_unlink;
+EXTERN_FUNC Func_utime _ws_utime;
+EXTERN_FUNC Func_utimes _ws_utimes;
+
+EXTERN_FUNC Func_execl _ws_execl;
+EXTERN_FUNC Func_execle _ws_execle;
+EXTERN_FUNC Func_execlp _ws_execlp;
+EXTERN_FUNC Func_execv _ws_execv;
+EXTERN_FUNC Func_execvp _ws_execvp;
+EXTERN_FUNC Func_fopen _ws_fopen;
+EXTERN_FUNC Func_fopen64 _ws_fopen64;
+EXTERN_FUNC Func_freopen _ws_freopen;
+EXTERN_FUNC Func_ftw _ws_ftw;
+EXTERN_FUNC Func_opendir _ws_opendir;
+EXTERN_FUNC Func_dlopen _ws_dlopen;
+
+EXTERN_FUNC Func_setpriority _ws_setpriority;
+//EXTERN_FUNC Func_pipe _ws_pipe;
+
+#define kMaxDisplays 4
+
+/*
+ * Global values.  Must be initialized in initGlobals(), which is executed
+ * the first time somebody calls dlopen on the wrapper lib.
+ */
+struct WrapSimGlobals {
+    volatile int    initialized;
+
+    /* descriptor where we write log messages */
+    int         logFd;
+
+    /* socket for communicating with simulator front-end */
+    int         simulatorFd;
+
+    /* coordinate thread startup */
+    pthread_mutex_t startLock;
+    pthread_cond_t  startCond;
+    int             startReady;
+    int             simulatorInitFailed;
+
+    /* base directory for filename remapping */
+    char*       remapBaseDir;
+    int         remapBaseDirLen;
+
+    /*
+     * Display characteristics.
+     *
+     * TODO: this is retrieved from the simulator during initial config.
+     * It needs to be visible to whatever process holds the surfaceflinger,
+     * which may or may not be the initial process in multi-process mode.
+     * We probably want to get the display config via a query, performed at
+     * intercepted-ioctl time, rather than a push from the sim at startup.
+     */
+    struct {
+        int     width;
+        int     height;
+
+        int     shmemKey;
+        int     shmid;
+        void*   addr;
+        long    length;
+        int     semid;
+    } display[kMaxDisplays];
+    int     numDisplays;
+
+    /*
+     * Input device.
+     */
+    FakeDev*    keyInputDevice;
+    const char *keyMap;
+
+    /* fake file descriptor allocation map */
+    pthread_mutex_t fakeFdLock;
+    BitVector*  fakeFdMap;
+    FakeDev*    fakeFdList[kMaxFakeFdCount];
+};
+
+extern struct WrapSimGlobals gWrapSim;
+
+#endif /*_WRAPSIM_GLOBALS_H*/
diff --git a/simulator/wrapsim/Init.c b/simulator/wrapsim/Init.c
new file mode 100644
index 0000000..eed650b
--- /dev/null
+++ b/simulator/wrapsim/Init.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Initialize the intercepts.
+ */
+#include "Common.h"
+
+#define __USE_GNU       /* need RTLD_NEXT */
+#include <dlfcn.h>
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+/*
+ * Global state.
+ */
+struct WrapSimGlobals gWrapSim;
+pthread_once_t gWrapSimInitialized = PTHREAD_ONCE_INIT;
+
+/*
+ * Initialize our global state.
+ */
+static void initGlobals(void)
+{
+    memset(&gWrapSim, 0xdd, sizeof(gWrapSim));
+    gWrapSim.logFd = -1;
+    gWrapSim.keyMap = NULL;
+
+    /*
+     * Find the original version of functions we override.
+     */
+    _ws_access = dlsym(RTLD_NEXT, "access");
+    _ws_open = dlsym(RTLD_NEXT, "open");
+    _ws_open64 = dlsym(RTLD_NEXT, "open64");
+
+    _ws_close = dlsym(RTLD_NEXT, "close");
+    _ws_read = dlsym(RTLD_NEXT, "read");
+    _ws_readv = dlsym(RTLD_NEXT, "readv");
+    _ws_write = dlsym(RTLD_NEXT, "write");
+    _ws_writev = dlsym(RTLD_NEXT, "writev");
+    _ws_mmap = dlsym(RTLD_NEXT, "mmap");
+    _ws_mmap64 = dlsym(RTLD_NEXT, "mmap64");
+    _ws_ioctl = dlsym(RTLD_NEXT, "ioctl");
+
+    _ws_chdir = dlsym(RTLD_NEXT, "chdir");
+    _ws_chmod = dlsym(RTLD_NEXT, "chmod");
+    _ws_chown = dlsym(RTLD_NEXT, "chown");
+    _ws_creat = dlsym(RTLD_NEXT, "creat");
+    _ws_execve = dlsym(RTLD_NEXT, "execve");
+    _ws_getcwd = dlsym(RTLD_NEXT, "getcwd");
+    _ws_lchown = dlsym(RTLD_NEXT, "lchown");
+    _ws_link = dlsym(RTLD_NEXT, "link");
+    _ws_lstat = dlsym(RTLD_NEXT, "lstat");
+    _ws_lstat64 = dlsym(RTLD_NEXT, "lstat64");
+    _ws___lxstat = dlsym(RTLD_NEXT, "__lxstat");
+    _ws___lxstat64 = dlsym(RTLD_NEXT, "__lxstat64");
+    _ws_mkdir = dlsym(RTLD_NEXT, "mkdir");
+    _ws_readlink = dlsym(RTLD_NEXT, "readlink");
+    _ws_rename = dlsym(RTLD_NEXT, "rename");
+    _ws_rmdir = dlsym(RTLD_NEXT, "rmdir");
+    _ws_stat = dlsym(RTLD_NEXT, "stat");
+    _ws_stat64 = dlsym(RTLD_NEXT, "stat64");
+    _ws___xstat = dlsym(RTLD_NEXT, "__xstat");
+    _ws___xstat64 = dlsym(RTLD_NEXT, "__xstat64");
+    _ws_statfs = dlsym(RTLD_NEXT, "statfs");
+    _ws_statfs64 = dlsym(RTLD_NEXT, "statfs64");
+    _ws_symlink = dlsym(RTLD_NEXT, "symlink");
+    _ws_unlink = dlsym(RTLD_NEXT, "unlink");
+    _ws_utime = dlsym(RTLD_NEXT, "utime");
+    _ws_utimes = dlsym(RTLD_NEXT, "utimes");
+
+    _ws_execl = dlsym(RTLD_NEXT, "execl");
+    _ws_execle = dlsym(RTLD_NEXT, "execle");
+    _ws_execlp = dlsym(RTLD_NEXT, "execlp");
+    _ws_execv = dlsym(RTLD_NEXT, "execv");
+    _ws_execvp = dlsym(RTLD_NEXT, "execvp");
+    _ws_fopen = dlsym(RTLD_NEXT, "fopen");
+    _ws_fopen64 = dlsym(RTLD_NEXT, "fopen64");
+    _ws_freopen = dlsym(RTLD_NEXT, "freopen");
+    _ws_ftw = dlsym(RTLD_NEXT, "ftw");
+    _ws_opendir = dlsym(RTLD_NEXT, "opendir");
+    _ws_dlopen = dlsym(RTLD_NEXT, "dlopen");
+
+    _ws_setpriority = dlsym(RTLD_NEXT, "setpriority");
+    //_ws_pipe = dlsym(RTLD_NEXT, "pipe");
+
+    const char* logFileName = getenv("WRAPSIM_LOG");
+    if (logFileName != NULL ){
+        gWrapSim.logFd = _ws_open(logFileName, O_WRONLY|O_APPEND|O_CREAT, 0664);
+    }
+
+    /* log messages now work; say hello */
+    wsLog("--- initializing sim wrapper ---\n");
+
+    gWrapSim.simulatorFd = -1;
+
+    pthread_mutex_init(&gWrapSim.startLock, NULL);
+    pthread_cond_init(&gWrapSim.startCond, NULL);
+    gWrapSim.startReady = 0;
+
+    pthread_mutex_init(&gWrapSim.fakeFdLock, NULL);
+    gWrapSim.fakeFdMap = wsAllocBitVector(kMaxFakeFdCount, 0);
+    memset(gWrapSim.fakeFdList, 0, sizeof(gWrapSim.fakeFdList));
+
+    gWrapSim.numDisplays = 0;
+
+    gWrapSim.keyInputDevice = NULL;
+
+    /*
+     * Get target for remapped "/system" and "/data".
+     *
+     * The ANDROID_PRODUCT_OUT env var *must* be set for rewriting to work.
+     */
+    const char* outEnv = getenv("ANDROID_PRODUCT_OUT");
+    if (outEnv == NULL) {
+        gWrapSim.remapBaseDir = NULL;
+        wsLog("--- $ANDROID_PRODUCT_OUT not set, "
+                "filename remapping disabled\n");
+    } else {
+        /* grab string and append '/' -- note this never gets freed */
+        gWrapSim.remapBaseDirLen = strlen(outEnv);
+        gWrapSim.remapBaseDir = strdup(outEnv);
+        wsLog("--- name remap to %s\n", gWrapSim.remapBaseDir);
+    }
+
+    gWrapSim.initialized = 1;
+}
+
+/*
+ * Creates a directory, or prints a log message if it fails.
+ */
+static int createTargetDirectory(const char *path, mode_t mode)
+{
+    int ret;
+
+    ret = mkdir(path, mode);
+    if (ret == 0 || errno == EEXIST) {
+        return 0;
+    }
+    wsLog("--- could not create target directory %s: %s\n",
+            path, strerror(errno));
+    return ret;
+}
+
+/*
+ * Any setup that would normally be done by init(8).
+ * Note that since the syscall redirects have been installed
+ * at this point, we are effectively operating within the
+ * simulation context.
+ */
+static void initGeneral(void)
+{
+    wsLog("--- preparing system\n");
+
+    /* Try to make sure that certain directories exist.
+     * If we fail to create them, the errors will show up in the log,
+     * but we keep going.
+     */
+    createTargetDirectory("/data", 0777);
+    createTargetDirectory("/data/dalvik-cache", 0777);
+}
+
+/*
+ * Initialize all necessary state, and indicate that we're ready to go.
+ */
+static void initOnce(void)
+{
+    initGlobals();
+    initGeneral();
+}
+
+/*
+ * Shared object initializer.  glibc guarantees that this function is
+ * called before dlopen() returns.  It may be called multiple times.
+ */
+__attribute__((constructor))
+static void initialize(void)
+{
+    pthread_once(&gWrapSimInitialized, initOnce);
+}
+
+
diff --git a/simulator/wrapsim/Intercept.c b/simulator/wrapsim/Intercept.c
new file mode 100644
index 0000000..29b0e00
--- /dev/null
+++ b/simulator/wrapsim/Intercept.c
@@ -0,0 +1,816 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Syscall and library intercepts.
+ */
+
+/* don't remap open() to open64() */
+#undef _FILE_OFFSET_BITS
+
+#define CREATE_FUNC_STORAGE
+#include "Common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <utime.h>
+#include <limits.h>
+#include <ftw.h>
+#include <assert.h>
+
+
+#if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64
+#warning "big"
+#endif
+
+//#define CALLTRACE(format, ...)   wsLog(format, ##__VA_ARGS__)
+#define CALLTRACE(format, ...)   ((void)0)
+
+//#define CALLTRACEV(format, ...)  wsLog(format, ##__VA_ARGS__)
+#define CALLTRACEV(format, ...)  ((void)0)
+
+/*
+When opening certain files, we need to simulate the contents.  For example,
+we can pretend to open the frame buffer, and respond to ioctl()s by
+returning fake data or telling the front-end to render new data.
+
+We want to intercept specific files in /dev.  In some cases we want to
+intercept and reject, e.g. to indicate that a standard Linux device does
+not exist.
+
+Some things we're not going to intercept:
+  /etc/... (e.g. /etc/timezone) -- std. Linux version should be okay
+  /proc/... (e.g. /proc/stat) -- we're showing real pid, so real proc will work
+
+For the device drivers we need to intercept:
+
+  close(), ioctl(), mmap(), open()/open64(), read(), readv(), write(),
+  writev()
+
+May also need stat().  We don't need all fd calls, e.g. fchdir() is
+not likely to succeed on a device driver.  The expected uses of mmap()
+shouldn't require intercepting related calls like madvise() -- we will
+provide an actual mapping of the requested size.  In some cases we will
+want to return a "real" fd so the app can poll() or select() on it.
+
+
+We also need to consider:
+  getuid/setuid + variations -- fake out multi-user-id stuff
+
+
+We also want to translate filenames, effectively performing a "chroot"
+without all the baggage that comes with it.  The mapping looks like:
+
+  /system/... --> $ANDROID_PRODUCT_OUT/system/...
+  /data/... --> $ANDROID_PRODUCT_OUT/data/...
+
+Translating pathnames requires interception of additional system calls,
+substituting a new path.  Calls include:
+
+  access(), chdir(), chmod(), chown(), creat(), execve(), getcwd(),
+  lchown(), link(), lstat()/lstat64(), mkdir(), open()/open64(),
+  readlink(), rename(), rmdir(), stat()/stat64(), statfs/statfs64(),
+  symlink(), unlink(), utimes(),
+
+Possibly also mknod(), mount(), umount().
+
+The "at" family, notably openat(), should just work since the root comes
+from an open directory fd.
+
+We also need these libc calls, because LD_LIBRARY_PATH substitutes at
+the libc link level, not the syscall layer:
+
+  execl(), execlp(), execle(), execv(), execvp(), fopen(), ftw(), getwd(),
+  opendir(), dlopen()
+
+It is possible for the cwd to leak out.  Some possible leaks:
+  - /proc/[self]/exe
+  - /proc/[self]/cwd
+  - LD_LIBRARY_PATH (which may be awkward to work around)
+
+
+To provide a replacement for the dirent functions -- only required if we
+want to show "fake" directory contents -- we would need:
+
+  closedir(), dirfd() readdir(), rewinddir(), scandir(), seekdir(),
+  telldir()
+
+
+*/
+
+
+/*
+ * ===========================================================================
+ *      Filename remapping
+ * ===========================================================================
+ */
+
+/*
+ * If appropriate, rewrite the path to point to a different location.
+ *
+ * Returns either "pathBuf" or "origPath" depending on whether or not we
+ * chose to rewrite the path.  "origPath" must be a buffer capable of
+ * holding an extended pathname; for best results use PATH_MAX.
+ */
+static const char* rewritePath(const char* func, char* pathBuf,
+    const char* origPath)
+{
+    /*
+     * Rewrite paths that start with "/system/" or "/data/"
+     */
+    if (origPath[0] != '/')
+        goto skip_rewrite;
+    if (memcmp(origPath+1, "system", 6) == 0 &&
+        (origPath[7] == '/' || origPath[7] == '\0'))
+            goto do_rewrite;
+    if (memcmp(origPath+1, "data", 4) == 0 &&
+        (origPath[5] == '/' || origPath[5] == '\0'))
+            goto do_rewrite;
+
+skip_rewrite:
+    /* check to see if something is side-stepping the rewrite */
+    if (memcmp(origPath, gWrapSim.remapBaseDir, gWrapSim.remapBaseDirLen) == 0)
+    {
+        wsLog("NOTE: full path used: %s(%s)\n", func, origPath);
+    }
+
+    CALLTRACE("rewrite %s('%s') --> (not rewritten)\n", func, origPath);
+    return origPath;
+
+do_rewrite:
+    memcpy(pathBuf, gWrapSim.remapBaseDir, gWrapSim.remapBaseDirLen);
+    strcpy(pathBuf + gWrapSim.remapBaseDirLen, origPath);
+    CALLTRACE("rewrite %s('%s') --> '%s'\n", func, origPath, pathBuf);
+    return pathBuf;
+}
+
+/*
+ * This works if the pathname is the first argument to the function, and
+ * the function returns "int".
+ */
+#define PASS_THROUGH_DECL(_fname, _rtype, ...)                              \
+    _rtype _fname( __VA_ARGS__ )
+#define PASS_THROUGH_BODY(_fname, _patharg, ...)                            \
+    {                                                                       \
+        CALLTRACEV("%s\n", __FUNCTION__);                                   \
+        char pathBuf[PATH_MAX];                                             \
+        return _ws_##_fname(rewritePath(#_fname, pathBuf, _patharg),        \
+            ##__VA_ARGS__);                                                 \
+    }
+
+
+PASS_THROUGH_DECL(chdir, int, const char* path)
+PASS_THROUGH_BODY(chdir, path)
+
+PASS_THROUGH_DECL(chmod, int, const char* path, mode_t mode)
+PASS_THROUGH_BODY(chmod, path, mode)
+
+PASS_THROUGH_DECL(chown, int, const char* path, uid_t owner, gid_t group)
+PASS_THROUGH_BODY(chown, path, owner, group)
+
+PASS_THROUGH_DECL(creat, int, const char* path, mode_t mode)
+PASS_THROUGH_BODY(creat, path, mode)
+
+PASS_THROUGH_DECL(execve, int, const char* path, char* const argv[],
+    char* const envp[])
+PASS_THROUGH_BODY(execve, path, argv, envp)
+
+PASS_THROUGH_DECL(lchown, int, const char* path, uid_t owner, gid_t group)
+PASS_THROUGH_BODY(lchown, path, owner, group)
+
+PASS_THROUGH_DECL(lstat, int, const char* path, struct stat* buf)
+PASS_THROUGH_BODY(lstat, path, buf)
+
+PASS_THROUGH_DECL(lstat64, int, const char* path, struct stat* buf)
+PASS_THROUGH_BODY(lstat64, path, buf)
+
+PASS_THROUGH_DECL(mkdir, int, const char* path, mode_t mode)
+PASS_THROUGH_BODY(mkdir, path, mode)
+
+PASS_THROUGH_DECL(readlink, ssize_t, const char* path, char* buf, size_t bufsiz)
+PASS_THROUGH_BODY(readlink, path, buf, bufsiz)
+
+PASS_THROUGH_DECL(rmdir, int, const char* path)
+PASS_THROUGH_BODY(rmdir, path)
+
+PASS_THROUGH_DECL(stat, int, const char* path, struct stat* buf)
+PASS_THROUGH_BODY(stat, path, buf)
+
+PASS_THROUGH_DECL(stat64, int, const char* path, struct stat* buf)
+PASS_THROUGH_BODY(stat64, path, buf)
+
+PASS_THROUGH_DECL(statfs, int, const char* path, struct statfs* buf)
+PASS_THROUGH_BODY(statfs, path, buf)
+
+PASS_THROUGH_DECL(statfs64, int, const char* path, struct statfs* buf)
+PASS_THROUGH_BODY(statfs64, path, buf)
+
+PASS_THROUGH_DECL(unlink, int, const char* path)
+PASS_THROUGH_BODY(unlink, path)
+
+PASS_THROUGH_DECL(utime, int, const char* path, const struct utimbuf* buf)
+PASS_THROUGH_BODY(utime, path, buf)
+
+PASS_THROUGH_DECL(utimes, int, const char* path, const struct timeval times[2])
+PASS_THROUGH_BODY(utimes, path, times)
+
+
+PASS_THROUGH_DECL(fopen, FILE*, const char* path, const char* mode)
+PASS_THROUGH_BODY(fopen, path, mode)
+
+PASS_THROUGH_DECL(fopen64, FILE*, const char* path, const char* mode)
+PASS_THROUGH_BODY(fopen64, path, mode)
+
+PASS_THROUGH_DECL(freopen, FILE*, const char* path, const char* mode,
+    FILE* stream)
+PASS_THROUGH_BODY(freopen, path, mode, stream)
+
+PASS_THROUGH_DECL(ftw, int, const char* dirpath,
+          int (*fn) (const char* fpath, const struct stat* sb, int typeflag),
+          int nopenfd)
+PASS_THROUGH_BODY(ftw, dirpath, fn, nopenfd)
+
+PASS_THROUGH_DECL(opendir, DIR*, const char* path)
+PASS_THROUGH_BODY(opendir, path)
+
+PASS_THROUGH_DECL(dlopen, void*, const char* path, int flag)
+PASS_THROUGH_BODY(dlopen, path, flag)
+
+/*
+ * Opposite of path translation -- remove prefix.
+ *
+ * It looks like BSD allows you to pass a NULL value for "buf" to inspire
+ * getcwd to allocate storage with malloc() (as an extension to the POSIX
+ * definition, which doesn't specify this).  getcwd() is a system call
+ * under Linux, so this doesn't work, but that doesn't stop gdb from
+ * trying to use it anyway.
+ */
+char* getcwd(char* buf, size_t size)
+{
+    CALLTRACEV("%s %p %d\n", __FUNCTION__, buf, size);
+
+    char* result = _ws_getcwd(buf, size);
+    if (buf != NULL && result != NULL) {
+        if (memcmp(buf, gWrapSim.remapBaseDir,
+                    gWrapSim.remapBaseDirLen) == 0)
+        {
+            memmove(buf, buf + gWrapSim.remapBaseDirLen,
+                strlen(buf + gWrapSim.remapBaseDirLen)+1);
+            CALLTRACE("rewrite getcwd() -> %s\n", result);
+        } else {
+            CALLTRACE("not rewriting getcwd(%s)\n", result);
+        }
+    }
+    return result;
+}
+
+/*
+ * Need to tweak both pathnames.
+ */
+int link(const char* oldPath, const char* newPath)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    char pathBuf1[PATH_MAX];
+    char pathBuf2[PATH_MAX];
+    return _ws_link(rewritePath("link-1", pathBuf1, oldPath),
+                    rewritePath("link-2", pathBuf2, newPath));
+}
+
+/*
+ * Need to tweak both pathnames.
+ */
+int rename(const char* oldPath, const char* newPath)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    char pathBuf1[PATH_MAX];
+    char pathBuf2[PATH_MAX];
+    return _ws_rename(rewritePath("rename-1", pathBuf1, oldPath),
+                      rewritePath("rename-2", pathBuf2, newPath));
+}
+
+/*
+ * Need to tweak both pathnames.
+ */
+int symlink(const char* oldPath, const char* newPath)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    char pathBuf1[PATH_MAX];
+    char pathBuf2[PATH_MAX];
+    return _ws_symlink(rewritePath("symlink-1", pathBuf1, oldPath),
+                       rewritePath("symlink-2", pathBuf2, newPath));
+}
+
+/*
+ * glibc stat turns into this (32-bit).
+ */
+int __xstat(int version, const char* path, struct stat* sbuf)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+    char pathBuf[PATH_MAX];
+    return _ws___xstat(version, rewritePath("__xstat", pathBuf, path),
+        sbuf);
+}
+
+/*
+ * glibc stat turns into this (64-bit).
+ */
+int __xstat64(int version, const char* path, struct stat* sbuf)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+    char pathBuf[PATH_MAX];
+    return _ws___xstat64(version, rewritePath("__xstat64", pathBuf, path),
+        sbuf);
+}
+
+/*
+ * glibc lstat turns into this (32-bit).
+ */
+int __lxstat(int version, const char* path, struct stat* sbuf)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+    char pathBuf[PATH_MAX];
+    return _ws___lxstat(version, rewritePath("__lxstat", pathBuf, path),
+        sbuf);
+}
+
+/*
+ * glibc lstat turns into this (64-bit).
+ */
+int __lxstat64(int version, const char* path, struct stat* sbuf)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+    char pathBuf[PATH_MAX];
+    return _ws___lxstat64(version, rewritePath("__lxstat64", pathBuf, path),
+        sbuf);
+}
+
+/*
+ * Copy the argument list out of varargs for execl/execlp/execle.  This
+ * leaves the argc value in _argc, and a NULL-terminated array of character
+ * pointers in _argv.  We stop at the first NULL argument, so we shouldn't
+ * end up copying "envp" out.
+ *
+ * We could use gcc __builtin_apply_args to just pass stuff through,
+ * but that may not get along with the path rewriting.  It's unclear
+ * whether we want to rewrite the first argument (i.e. the string that
+ * becomes argv[0]); it only makes sense if the exec'ed program is also
+ * getting remapped.
+ */
+#define COPY_EXEC_ARGLIST(_first, _argc, _argv)                             \
+    int _argc = 0;                                                          \
+    {                                                                       \
+        va_list vargs;                                                      \
+        va_start(vargs, _first);                                            \
+        while (1) {                                                         \
+            _argc++;                                                        \
+            const char* val = va_arg(vargs, const char*);                   \
+            if (val == NULL)                                                \
+                break;                                                      \
+        }                                                                   \
+        va_end(vargs);                                                      \
+    }                                                                       \
+    const char* _argv[_argc+1];                                             \
+    _argv[0] = _first;                                                      \
+    {                                                                       \
+        va_list vargs;                                                      \
+        int i;                                                              \
+        va_start(vargs, _first);                                            \
+        for (i = 1; i < _argc; i++) {                                       \
+            _argv[i] = va_arg(vargs, const char*);                          \
+        }                                                                   \
+        va_end(vargs);                                                      \
+    }                                                                       \
+    _argv[_argc] = NULL;
+
+/*
+ * Debug dump.
+ */
+static void dumpExecArgs(const char* callName, const char* path,
+    int argc, const char* argv[], char* const envp[])
+{
+    int i;
+
+    CALLTRACE("Calling %s '%s' (envp=%p)\n", callName, path, envp);
+    for (i = 0; i <= argc; i++)
+        CALLTRACE("  %d: %s\n", i, argv[i]);
+}
+
+/*
+ * Extract varargs, convert paths, hand off to execv.
+ */
+int execl(const char* path, const char* arg, ...)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    char pathBuf[PATH_MAX];
+
+    COPY_EXEC_ARGLIST(arg, argc, argv);
+    dumpExecArgs("execl", path, argc, argv, NULL);
+    path = rewritePath("execl", pathBuf, path);
+    return _ws_execv(path, (char* const*) argv);
+}
+
+/*
+ * Extract varargs, convert paths, hand off to execve.
+ *
+ * The execle prototype in the man page isn't valid C -- it shows the
+ * "envp" argument after the "...".  We have to pull it out with the rest
+ * of the varargs.
+ */
+int execle(const char* path, const char* arg, ...)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    char pathBuf[PATH_MAX];
+
+    COPY_EXEC_ARGLIST(arg, argc, argv);
+
+    /* run through again and find envp */
+    char* const* envp;
+
+    va_list vargs;
+    va_start(vargs, arg);
+    while (1) {
+        const char* val = va_arg(vargs, const char*);
+        if (val == NULL) {
+            envp = va_arg(vargs, char* const*);
+            break;
+        }
+    }
+    va_end(vargs);
+
+    dumpExecArgs("execle", path, argc, argv, envp);
+    path = rewritePath("execl", pathBuf, path);
+
+    return _ws_execve(path, (char* const*) argv, envp);
+}
+
+/*
+ * Extract varargs, convert paths, hand off to execvp.
+ */
+int execlp(const char* file, const char* arg, ...)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    char pathBuf[PATH_MAX];
+
+    COPY_EXEC_ARGLIST(arg, argc, argv);
+    dumpExecArgs("execlp", file, argc, argv, NULL);
+    file = rewritePath("execlp", pathBuf, file);
+    return _ws_execvp(file, (char* const*) argv);
+}
+
+/*
+ * Update path, forward to execv.
+ */
+int execv(const char* path, char* const argv[])
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    char pathBuf[PATH_MAX];
+
+    path = rewritePath("execv", pathBuf, path);
+    return _ws_execv(path, argv);
+}
+
+/*
+ * Shouldn't need to do anything unless they specified a full path to execvp.
+ */
+int execvp(const char* file, char* const argv[])
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    char pathBuf[PATH_MAX];
+
+    file = rewritePath("execvp", pathBuf, file);
+    return _ws_execvp(file, argv);
+}
+
+
+/*
+ * ===========================================================================
+ *      Device fakery
+ * ===========================================================================
+ */
+
+/*
+ * Need to do filesystem translation and show fake devices.
+ */
+int access(const char* pathName, int mode)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    int status = wsInterceptDeviceAccess(pathName, mode);
+    if (status == 0)
+        return 0;
+    else if (status == -2)
+        return -1;          // errno already set
+    else {
+        char pathBuf[PATH_MAX];
+        return _ws_access(rewritePath("access", pathBuf, pathName), mode);
+    }
+}
+
+/*
+ * Common handler for open().
+ */
+int openCommon(const char* pathName, int flags, mode_t mode)
+{
+    char pathBuf[PATH_MAX];
+    int fd;
+
+    assert(gWrapSim.initialized);
+
+    fd = wsInterceptDeviceOpen(pathName, flags);
+    if (fd >= 0) {
+        return fd;
+    } else if (fd == -2) {
+        /* errno should be set */
+        return -1;
+    }
+
+    if ((flags & O_CREAT) != 0) {
+        fd = _ws_open(rewritePath("open", pathBuf, pathName), flags, mode);
+        CALLTRACE("open(%s, 0x%x, 0%o) = %d\n", pathName, flags, mode, fd);
+    } else {
+        fd = _ws_open(rewritePath("open", pathBuf, pathName), flags, 0);
+        CALLTRACE("open(%s, 0x%x) = %d\n", pathName, flags, fd);
+    }
+    return fd;
+}
+
+/*
+ * Replacement open() and variants.
+ *
+ * We have to use the vararg decl for the standard call so it matches
+ * the definition in fcntl.h.
+ */
+int open(const char* pathName, int flags, ...)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    mode_t mode = 0;
+    if ((flags & O_CREAT) != 0) {
+        va_list args;
+
+        va_start(args, flags);
+        mode = va_arg(args, mode_t);
+        va_end(args);
+    }
+
+    return openCommon(pathName, flags, mode);
+}
+int __open(const char* pathName, int flags, mode_t mode)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    return openCommon(pathName, flags, mode);
+}
+
+/*
+ * Common handler for open64().
+ */
+int open64Common(const char* pathName, int flags, mode_t mode)
+{
+    char pathBuf[PATH_MAX];
+    int fd;
+
+    assert(gWrapSim.initialized);
+
+    fd = wsInterceptDeviceOpen(pathName, flags);
+    if (fd >= 0) {
+        return fd;
+    }
+
+    if ((flags & O_CREAT) != 0) {
+        fd = _ws_open64(rewritePath("open64", pathBuf, pathName), flags, mode);
+        CALLTRACE("open64(%s, 0x%x, 0%o) = %d\n", pathName, flags, mode, fd);
+    } else {
+        fd = _ws_open64(rewritePath("open64", pathBuf, pathName), flags, 0);
+        CALLTRACE("open64(%s, 0x%x) = %d\n", pathName, flags, fd);
+    }
+    return fd;
+}
+
+/*
+ * Replacement open64() and variants.
+ *
+ * We have to use the vararg decl for the standard call so it matches
+ * the definition in fcntl.h.
+ */
+int open64(const char* pathName, int flags, ...)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    mode_t mode = 0;
+    if ((flags & O_CREAT) != 0) {
+        va_list args;
+
+        va_start(args, flags);
+        mode = va_arg(args, mode_t);
+        va_end(args);
+    }
+    return open64Common(pathName, flags, mode);
+}
+int __open64(const char* pathName, int flags, mode_t mode)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    return open64Common(pathName, flags, mode);
+}
+
+
+/*
+ * Close a file descriptor.
+ */
+int close(int fd)
+{
+    CALLTRACEV("%s(%d)\n", __FUNCTION__, fd);
+
+    FakeDev* dev = wsFakeDevFromFd(fd);
+    if (dev != NULL) {
+        int result = dev->close(dev, fd);
+        wsFreeFakeDev(dev);
+        return result;
+    } else {
+        CALLTRACE("close(%d)\n", fd);
+        return _ws_close(fd);
+    }
+}
+
+/*
+ * Map a region.
+ */
+void* mmap(void* start, size_t length, int prot, int flags, int fd,
+    __off_t offset)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    FakeDev* dev = wsFakeDevFromFd(fd);
+    if (dev != NULL) {
+        return dev->mmap(dev, start, length, prot, flags, fd, offset);
+    } else {
+        CALLTRACE("mmap(%p, %d, %d, %d, %d, %d)\n",
+            start, (int) length, prot, flags, fd, (int) offset);
+        return _ws_mmap(start, length, prot, flags, fd, offset);
+    }
+}
+
+/*
+ * Map a region.
+ */
+void* mmap64(void* start, size_t length, int prot, int flags, int fd,
+    __off64_t offset)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    FakeDev* dev = wsFakeDevFromFd(fd);
+    if (dev != NULL) {
+        return dev->mmap(dev, start, length, prot, flags, fd, (__off_t) offset);
+    } else {
+        CALLTRACE("mmap64(%p, %d, %d, %d, %d, %d)\n",
+            start, (int) length, prot, flags, fd, (int) offset);
+        return _ws_mmap(start, length, prot, flags, fd, offset);
+    }
+}
+
+/*
+ * The Linux headers show this with a vararg header, but as far as I can
+ * tell the kernel always expects 3 args.
+ */
+int ioctl(int fd, int request, ...)
+{
+    CALLTRACEV("%s(%d, %d, ...)\n", __FUNCTION__, fd, request);
+
+    FakeDev* dev = wsFakeDevFromFd(fd);
+    va_list args;
+    void* argp;
+
+    /* extract argp from varargs */
+    va_start(args, request);
+    argp = va_arg(args, void*);
+    va_end(args);
+
+    if (dev != NULL) {
+        return dev->ioctl(dev, fd, request, argp);
+    } else {
+        CALLTRACE("ioctl(%d, 0x%x, %p)\n", fd, request, argp);
+        return _ws_ioctl(fd, request, argp);
+    }
+}
+
+/*
+ * Read data.
+ */
+ssize_t read(int fd, void* buf, size_t count)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    FakeDev* dev = wsFakeDevFromFd(fd);
+    if (dev != NULL) {
+        return dev->read(dev, fd, buf, count);
+    } else {
+        CALLTRACE("read(%d, %p, %u)\n", fd, buf, count);
+        return _ws_read(fd, buf, count);
+    }
+}
+
+/*
+ * Write data.
+ */
+ssize_t write(int fd, const void* buf, size_t count)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    FakeDev* dev = wsFakeDevFromFd(fd);
+    if (dev != NULL) {
+        return dev->write(dev, fd, buf, count);
+    } else {
+        CALLTRACE("write(%d, %p, %u)\n", fd, buf, count);
+        return _ws_write(fd, buf, count);
+    }
+}
+
+/*
+ * Read a data vector.
+ */
+ssize_t readv(int fd, const struct iovec* vector, int count)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    FakeDev* dev = wsFakeDevFromFd(fd);
+    if (dev != NULL) {
+        return dev->readv(dev, fd, vector, count);
+    } else {
+        CALLTRACE("readv(%d, %p, %u)\n", fd, vector, count);
+        return _ws_readv(fd, vector, count);
+    }
+}
+
+/*
+ * Write a data vector.
+ */
+ssize_t writev(int fd, const struct iovec* vector, int count)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    FakeDev* dev = wsFakeDevFromFd(fd);
+    if (dev != NULL) {
+        return dev->writev(dev, fd, vector, count);
+    } else {
+        CALLTRACE("writev(%d, %p, %u)\n", fd, vector, count);
+        return _ws_writev(fd, vector, count);
+    }
+}
+
+/*
+ * Set the scheduling priority.  The sim doesn't run as root, so we have
+ * to fake this out.
+ *
+ * For now, do some basic verification of the which and who parameters,
+ * but otherwise return success.  In the future we may want to track
+ * these so getpriority works.
+ */
+int setpriority(__priority_which_t which, id_t who, int what)
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    if (which != PRIO_PROCESS &&
+        which != PRIO_PGRP &&
+        which != PRIO_USER) {
+        return EINVAL;
+    }
+
+    if ((int)who < 0) {
+        return ESRCH;
+    }
+
+    return 0;
+}
+
+#if 0
+/*
+ * Create a pipe.  (Only needed for debugging an fd leak.)
+ */
+int pipe(int filedes[2])
+{
+    CALLTRACEV("%s\n", __FUNCTION__);
+
+    int result = _ws_pipe(filedes);
+    if (result == 0)
+        CALLTRACE("pipe(%p) -> %d,%d\n", filedes, filedes[0], filedes[1]);
+    if (filedes[0] == 83)
+        abort();
+    return result;
+}
+#endif
diff --git a/simulator/wrapsim/LaunchWrapper.c b/simulator/wrapsim/LaunchWrapper.c
new file mode 100644
index 0000000..c4f0efb
--- /dev/null
+++ b/simulator/wrapsim/LaunchWrapper.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Launch the specified program and, if "-wait" was specified, wait for it
+ * to exit.
+ *
+ * When in "wait mode", print a message indicating the exit status, then
+ * wait for Ctrl-C before we exit.  This is useful if we were launched
+ * with "xterm -e", because it lets us see the output before the xterm bails.
+ *
+ * We want to ignore signals while waiting, so Ctrl-C kills the child rather
+ * than us, but we need to configure the signals *after* the fork() so we
+ * don't block them for the child too.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+/*
+ * This is appended to $ANDROID_PRODUCT_OUT,
+ * e.g. "/work/device/out/debug/host/linux-x8x/product/sim".
+ */
+static const char* kWrapLib = "/system/lib/libwrapsim.so";
+
+
+/*
+ * Configure LD_PRELOAD if possible.
+ *
+ * Returns newly-allocated storage with the preload path.
+ */
+static char* configurePreload(void)
+{
+    const char* outEnv = getenv("ANDROID_PRODUCT_OUT");
+    const char* preloadEnv = getenv("LD_PRELOAD");
+    char* preload = NULL;
+
+    if (preloadEnv != NULL) {
+        /* TODO: append our stuff to existing LD_PRELOAD string */
+        fprintf(stderr,
+            "LW WARNING: LD_PRELOAD already set, not adding libwrapsim\n");
+    } else if (outEnv == NULL || *outEnv == '\0') {
+        fprintf(stderr, "LW WARNING: "
+            "$ANDROID_PRODUCT_OUT not in env, not setting LD_PRELOAD\n");
+    } else {
+        preload = (char*) malloc(strlen(outEnv) + strlen(kWrapLib) +1);
+        sprintf(preload, "%s%s", outEnv, kWrapLib);
+        setenv("LD_PRELOAD", preload, 1);
+        printf("LW: launching with LD_PRELOAD=%s\n", preload);
+    }
+
+    /* Let the process know that it's executing inside this LD_PRELOAD
+     * wrapper.
+     */
+    setenv("ANDROID_WRAPSIM", "1", 1);
+
+    return preload;
+}
+
+/*
+ * Configure some environment variables that the runtime wants.
+ *
+ * Returns 0 if all goes well.
+ */
+static int configureEnvironment()
+{
+    const char* outEnv = getenv("ANDROID_PRODUCT_OUT");
+    char pathBuf[PATH_MAX];
+    int outLen;
+
+    if (outEnv == NULL || *outEnv == '\0') {
+        fprintf(stderr, "LW WARNING: "
+            "$ANDROID_PRODUCT_OUT not in env, not configuring environment\n");
+        return 1;
+    }
+    outLen = strlen(outEnv);
+    assert(outLen + 64 < PATH_MAX);
+    memcpy(pathBuf, outEnv, outLen);
+    strcpy(pathBuf + outLen, "/system/lib");
+
+    /*
+     * Linux wants LD_LIBRARY_PATH
+     * Mac OS X wants DYLD_LIBRARY_PATH
+     * gdb under Mac OS X tramples on both of the above, so we added
+     * ANDROID_LIBRARY_PATH as a workaround.
+     *
+     * We're only supporting Linux now, so just set LD_LIBRARY_PATH.  Note
+     * this stomps the existing value, if any.
+     *
+     * If we only needed this for System.loadLibrary() we could do it later,
+     * but we need it to get the runtime started.
+     */
+    printf("LW: setting LD_LIBRARY_PATH=%s\n", pathBuf);
+    setenv("LD_LIBRARY_PATH", pathBuf, 1);
+
+    /*
+     * Trusted certificates are found, for some bizarre reason, through
+     * the JAVA_HOME environment variable.  We don't need to set this
+     * here, but it's convenient to do so.
+     */
+    strcpy(pathBuf /*+ outLen*/, "/system");
+    printf("LW: setting JAVA_HOME=%s\n", pathBuf);
+    setenv("JAVA_HOME", pathBuf, 1);
+
+    return 0;
+}
+
+/*
+ * Redirect stdout/stderr to the specified file.  If "fileName" is NULL,
+ * this returns successfully without doing anything.
+ *
+ * Returns 0 on success.
+ */
+static int redirectStdio(const char* fileName)
+{
+    int fd;
+
+    if (fileName == NULL)
+        return 0;
+
+    printf("Redirecting stdio to append to '%s'\n", fileName);
+    fflush(stdout);
+    fflush(stderr);
+
+    fd = open(fileName, O_WRONLY | O_APPEND | O_CREAT, 0666);
+    if (fd < 0) {
+        fprintf(stderr, "ERROR: unable to open '%s' for writing\n",
+            fileName);
+        return 1;
+    }
+    dup2(fd, 1);
+    dup2(fd, 2);
+    close(fd);
+
+    return 0;
+}
+
+/*
+ * Launch the requested process directly.
+ *
+ * On success this does not return (ever).
+ */
+static int launch(char* argv[], const char* outputFile)
+{
+    (void) configurePreload();
+    (void) redirectStdio(outputFile);
+    execvp(argv[0], argv);
+    fprintf(stderr, "execvp %s failed: %s\n", argv[0], strerror(errno));
+    return 1;
+}
+
+/*
+ * Launch in a sub-process and wait for it to finish.
+ */
+static int launchWithWait(char* argv[], const char* outputFile)
+{
+    pid_t child;
+
+    child = fork();
+    if (child < 0) {
+        fprintf(stderr, "fork() failed: %s\n", strerror(errno));
+        return 1;
+    } else if (child == 0) {
+        /*
+         * This is the child, set up LD_PRELOAD if possible and launch.
+         */
+        (void) configurePreload();
+        (void) redirectStdio(outputFile);
+        execvp(argv[0], argv);
+        fprintf(stderr, "execvp %s failed: %s\n", argv[0], strerror(errno));
+        return 1;
+    } else {
+        pid_t result;
+        int status;
+
+        signal(SIGINT, SIG_IGN);
+        signal(SIGQUIT, SIG_IGN);
+
+        while (1) {
+            printf("LW: in pid %d (grp=%d), waiting on pid %d\n",
+                (int) getpid(), (int) getpgrp(), (int) child);
+            result = waitpid(child, &status, 0);
+            if (result < 0) {
+                if (errno == EINTR) {
+                    printf("Hiccup!\n");
+                    continue;
+                } else {
+                    fprintf(stderr, "waitpid failed: %s\n", strerror(errno));
+                    return 1;
+                }
+            } else if (result != child) {
+                fprintf(stderr, "bizarre: waitpid returned %d (wanted %d)\n",
+                    result, child);
+                return 1;
+            } else {
+                break;
+            }
+        }
+
+        printf("\n");
+        if (WIFEXITED(status)) {
+            printf("LW: process exited (status=%d)", WEXITSTATUS(status));
+        } else if (WIFSIGNALED(status)) {
+            printf("LW: process killed by signal %d", WTERMSIG(status));
+        } else {
+            printf("LW: process freaked out, status=0x%x\n", status);
+        }
+        if (WCOREDUMP(status)) {
+            printf(" (core dumped)");
+        }
+        printf("\n");
+
+        signal(SIGINT, SIG_DFL);
+        signal(SIGQUIT, SIG_DFL);
+
+        /*
+         * The underlying process may have changed process groups and pulled
+         * itself into the foreground.  Now that it's gone, pull ourselves
+         * back into the foreground.
+         */
+        signal(SIGTTOU, SIG_IGN);
+        if (tcsetpgrp(fileno(stdin), getpgrp()) != 0)
+            fprintf(stderr, "WARNING: tcsetpgrp failed\n");
+
+        printf("\nHit Ctrl-C or close window.\n");
+
+        while (1) {
+            sleep(10);
+        }
+
+        /* not reached */
+        return 0;
+    }
+}
+
+
+/*
+ * All args are passed through.
+ */
+int main(int argc, char** argv)
+{
+    int waitForChild = 0;
+    const char* outputFile = NULL;
+    int result;
+
+    /*
+     * Skip past the reference to ourselves, and check for args.
+     */
+    argv++;
+    argc--;
+    while (argc > 0) {
+        if (strcmp(argv[0], "-wait") == 0) {
+            waitForChild = 1;
+        } else if (strcmp(argv[0], "-output") == 0 && argc > 1) {
+            argv++;
+            argc--;
+            outputFile = argv[0];
+        } else {
+            /* no more args for us */
+            break;
+        }
+
+        argv++;
+        argc--;
+    }
+
+    if (argc == 0) {
+        fprintf(stderr,
+            "Usage: launch-wrapper [-wait] [-output filename] <cmd> [args...]\n");
+        result = 2;
+        goto bail;
+    }
+
+    /*
+     * Configure some environment variables.
+     */
+    if (configureEnvironment() != 0) {
+        result = 1;
+        goto bail;
+    }
+
+    /*
+     * Launch.
+     */
+    if (waitForChild)
+        result = launchWithWait(argv, outputFile);
+    else
+        result = launch(argv, outputFile);
+
+bail:
+    if (result != 0)
+        sleep(2);
+    return result;
+}
+
diff --git a/simulator/wrapsim/Log.c b/simulator/wrapsim/Log.c
new file mode 100644
index 0000000..7edb677
--- /dev/null
+++ b/simulator/wrapsim/Log.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Debug-logging code.
+ */
+#include "Common.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+
+/*
+ * Write a message to our private log file.  This is a little awkward since
+ * some or all of the system calls we want to use are being intercepted.
+ */
+void wsLog(const char* format, ...)
+{
+#if defined(HAVE_LOCALTIME_R)
+    struct tm tmBuf;
+#endif
+    struct tm* ptm;
+    time_t now;
+    char timeBuf[32];
+    char prefixBuf[64];
+    int prefixLen;
+    char msgBuf[256];
+    int msgLen;
+
+    if (gWrapSim.logFd < 0)
+        return;
+
+    /*
+     * Create a prefix with a timestamp.
+     */
+    now = time(NULL);
+#if defined(HAVE_LOCALTIME_R)
+    ptm = localtime_r(&now, &tmBuf);
+#else
+    ptm = localtime(&now);
+#endif
+    //strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+    strftime(timeBuf, sizeof(timeBuf), "%H:%M:%S", ptm);
+
+    prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s %5d ",
+        timeBuf, (int) getpid());
+
+    /*
+     * Format the message into a buffer.
+     */
+    va_list args;
+
+    va_start(args, format);
+    msgLen = vsnprintf(msgBuf, sizeof(msgBuf), format, args);
+    va_end(args);
+
+    /* if we overflowed, trim and annotate */
+    if (msgLen >= (int) sizeof(msgBuf)) {
+        msgBuf[sizeof(msgBuf)-2] = '!';
+        msgBuf[sizeof(msgBuf)-1] = '\n';
+        msgLen = sizeof(msgBuf);
+    }
+
+    /*
+     * Write the whole thing in one shot.  The log file was opened with
+     * O_APPEND so we don't have to worry about clashes.
+     */
+    struct iovec logVec[2];
+    logVec[0].iov_base = prefixBuf;
+    logVec[0].iov_len = prefixLen;
+    logVec[1].iov_base = msgBuf;
+    logVec[1].iov_len = msgLen;
+    (void) _ws_writev(gWrapSim.logFd, logVec, 2);
+}
+
diff --git a/simulator/wrapsim/Log.h b/simulator/wrapsim/Log.h
new file mode 100644
index 0000000..024ba44
--- /dev/null
+++ b/simulator/wrapsim/Log.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Logging.
+ */
+#ifndef _WRAPSIM_LOG_H
+#define _WRAPSIM_LOG_H
+
+
+/*
+ * Log debug info.
+ */
+void wsLog(const char* format, ...)
+    #if defined(__GNUC__)
+        __attribute__ ((format(printf, 1, 2)))
+    #endif
+    ;
+
+#endif /*_WRAPSIM_LOG_H*/
diff --git a/simulator/wrapsim/README.txt b/simulator/wrapsim/README.txt
new file mode 100644
index 0000000..358c06c
--- /dev/null
+++ b/simulator/wrapsim/README.txt
@@ -0,0 +1,21 @@
+This shared library is used with LD_PRELOAD to wrap the Android runtime
+when used with the desktop simulator.
+
+Because LD_PRELOAD is part of the environment when gdb and valgrind are
+invoked, the wrapper can "see" activity from both of these (more so gdb
+than valgrind).  For this reason it needs to be very careful about which
+"open" calls are intercepted.
+
+It's also important that none of the intercepted system or library calls
+are invoked directly, or infinite recursion could result.
+
+Avoid creating dependencies on other libraries.
+
+
+To debug wrapsim, set WRAPSIM_LOG to a log file before launching, e.g.
+
+% WRAPSIM_LOG=/tmp/wraplog.txt simulator
+
+For more verbose logging, you can enable the verbose forms of CALLTRACE
+and CALLTRACEV in Intercept.c.
+
diff --git a/simulator/wrapsim/SimMgr.c b/simulator/wrapsim/SimMgr.c
new file mode 100644
index 0000000..a35c7c6
--- /dev/null
+++ b/simulator/wrapsim/SimMgr.c
@@ -0,0 +1,988 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Simulator interactions.
+ *
+ * TODO: for multi-process we probably need to switch to a new process
+ * group if we are the first process (could be runtime, could be gdb),
+ * rather than wait for the simulator to tell us to switch.
+ */
+#include "Common.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/sem.h>
+#include <sys/un.h>
+#include <signal.h>
+#include <assert.h>
+
+// fwd
+static int connectToSim(void);
+static void listenToSim(void);
+
+/*
+ * Env var to restrict who tries to talk to the front end.
+ */
+#define kWrapSimConnectedEnv    "WRAP_SIM_CONNECTED"
+
+
+/*
+ * Signal the main thread that we're ready to continue.
+ */
+static void signalMainThread(void)
+{
+    int cc;
+
+    cc = pthread_mutex_lock(&gWrapSim.startLock);
+    assert(cc == 0);
+
+    gWrapSim.startReady = 1;
+
+    cc = pthread_cond_signal(&gWrapSim.startCond);
+    assert(cc == 0);
+
+    cc = pthread_mutex_unlock(&gWrapSim.startLock);
+    assert(cc == 0);
+}
+
+
+/*
+ * Entry point for the sim management thread.
+ *
+ * Once we have established a connection to the simulator and are ready
+ * for other threads to send messages, we signal the main thread.
+ */
+static void* simThreadEntry(void* arg)
+{
+    wsLog("--- sim manager thread started\n");
+
+    /*
+     * Establish a connection to the simulator front-end.  If we can't do
+     * that, we have no access to input or output devices, and we might
+     * as well give up.
+     */
+    if (connectToSim() != 0) {
+        signalMainThread();
+        return NULL;
+    }
+
+    /* success! */
+    wsLog("--- sim manager thread ready\n");
+    gWrapSim.simulatorInitFailed = 0;
+    signalMainThread();
+
+    listenToSim();
+
+    wsLog("--- sim manager thread exiting\n");
+
+    return NULL;
+}
+
+/*
+ * If we think we're not yet connected to the sim, do so now.  We only
+ * want to do this once per process *group*, so we control access with
+ * an environment variable.
+ */
+int wsSimConnect(void)
+{
+    /*
+     * If the environment variable hasn't been set, assume we're the first
+     * to get here, and should attach to the simulator.  We set the env
+     * var here whether or not we succeed in connecting to the sim.
+     *
+     * (For correctness we should wrap the getenv/setenv in a semaphore.)
+     */
+    if (getenv(kWrapSimConnectedEnv) == NULL) {
+        pthread_attr_t threadAttr;
+        pthread_t threadHandle;
+        int cc;
+
+        gWrapSim.simulatorInitFailed = 1;
+        setenv(kWrapSimConnectedEnv, "1", 1);
+
+        cc = pthread_mutex_lock(&gWrapSim.startLock);
+        assert(cc == 0);
+
+        pthread_attr_init(&threadAttr);
+        pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
+        cc = pthread_create(&threadHandle, &threadAttr, simThreadEntry, NULL);
+        if (cc != 0) {
+            wsLog("Unable to create new thread: %s\n", strerror(errno));
+            abort();
+        }
+
+        while (!gWrapSim.startReady) {
+            cc = pthread_cond_wait(&gWrapSim.startCond, &gWrapSim.startLock);
+            assert(cc == 0);
+        }
+
+        cc = pthread_mutex_unlock(&gWrapSim.startLock);
+        assert(cc == 0);
+
+        if (gWrapSim.simulatorInitFailed) {
+            wsLog("Simulator initialization failed, bailing\n");
+
+            /* this *should* be okay to do */
+            fprintf(stderr, "Fatal error:"
+                " unable to connect to sim front-end (not running?)\n");
+            abort();
+        }
+    }
+
+    wsLog("+++ continuing\n");
+    return 0;
+}
+
+
+/*
+ * ===========================================================================
+ *      Message / MessageStream
+ * ===========================================================================
+ */
+
+/*
+ * This is a quick & dirty rewrite of the C++ Message and MessageStream
+ * classes, ported to C, reduced in generality, with syscalls stubbed
+ * where necessary.  I didn't fix the API to make it more sensible in C
+ * (which lacks destructors), so some of this is a little fragile or
+ * awkward.
+ */
+
+/* values for message type byte; must match android::Message constants */
+typedef enum MessageType {
+    kTypeUnknown = 0,
+    kTypeRaw,           // chunk of raw data
+    kTypeConfig,        // send a name=value pair to peer
+    kTypeCommand,       // simple command w/arg
+    kTypeCommandExt,    // slightly more complicated command
+    kTypeLogBundle,     // multi-part log message
+} MessageType;
+
+/*
+ * Reusable message object.
+ */
+typedef struct Message {
+    MessageType     mType;
+    unsigned char*  mData;
+    int             mLength;
+} Message;
+
+/* magic init messages; must match android::MessageStream constants */
+enum {
+    kHelloMsg       = 0x4e303047,       // 'N00G'
+    kHelloAckMsg    = 0x31455221,       // '1ER!'
+};
+
+
+/*
+ * Clear out a Message.
+ */
+static void Message_clear(Message* msg)
+{
+    memset(msg, 0, sizeof(Message));
+}
+
+/*
+ * Keep reading until we get all bytes or hit EOF/error.  "fd" is expected
+ * to be in blocking mode.
+ *
+ * Returns 0 on success.
+ */
+static int readAll(int fd, void* buf, size_t count)
+{
+    ssize_t actual;
+    ssize_t have;
+
+    have = 0;
+    while (have != (ssize_t) count) {
+        actual = _ws_read(fd, ((char*) buf) + have, count - have);
+        if (actual < 0) {
+            if (errno == EINTR)
+                continue;
+            wsLog("read %d failed: %s\n", fd, strerror(errno));
+        } else if (actual == 0) {
+            wsLog("early EOF on %d\n", fd);
+            return -1;
+        } else {
+            have += actual;
+        }
+
+        assert(have <= (ssize_t)count);
+    }
+
+    return 0;
+}
+
+#if 0
+/*
+ * Keep writing until we put all bytes or hit an error.  "fd" is expected
+ * to be in blocking mode.
+ *
+ * Returns 0 on success.
+ */
+static int writeAll(int fd, const void* buf, size_t count)
+{
+    ssize_t actual;
+    ssize_t have;
+
+    have = 0;
+    while (have != count) {
+        actual = _ws_write(fd, ((const char*) buf) + have, count - have);
+        if (actual < 0) {
+            if (errno == EINTR)
+                continue;
+            wsLog("write %d failed: %s\n", fd, strerror(errno));
+        } else if (actual == 0) {
+            wsLog("wrote zero on %d\n", fd);
+            return -1;
+        } else {
+            have += actual;
+        }
+
+        assert(have <= count);
+    }
+
+    return 0;
+}
+#endif
+
+/*
+ * Read a message from the specified file descriptor.
+ *
+ * The caller must Message_release(&msg).
+ *
+ * We guarantee 32-bit alignment for msg->mData.
+ */
+static int Message_read(Message* msg, int fd)
+{
+    unsigned char header[4];
+
+    readAll(fd, header, 4);
+
+    msg->mType = (MessageType) header[2];
+    msg->mLength = header[0] | header[1] << 8;
+    msg->mLength -= 2;   // we already read two of them in the header
+
+    if (msg->mLength > 0) {
+        int actual;
+
+        /* Linux malloc guarantees at least 32-bit alignment */
+        msg->mData = (unsigned char*) malloc(msg->mLength);
+        if (msg->mData == NULL) {
+            wsLog("alloc %d failed\n", msg->mLength);
+            return -1;
+        }
+
+        if (readAll(fd, msg->mData, msg->mLength) != 0) {
+            wsLog("failed reading message body (wanted %d)\n", msg->mLength);
+            return -1;
+        }
+    } else {
+        msg->mData = NULL;
+    }
+
+    return 0;
+}
+
+/*
+ * Write a message to the specified file descriptor.
+ *
+ * The caller must Message_release(&msg).
+ */
+static int Message_write(Message* msg, int fd)
+{
+    struct iovec writeVec[2];
+    unsigned char header[4];
+    int len, numVecs;
+    ssize_t actual;
+
+    len = msg->mLength + 2;
+    header[0] = len & 0xff;
+    header[1] = (len >> 8) & 0xff;
+    header[2] = msg->mType;
+    header[3] = 0;
+    writeVec[0].iov_base = header;
+    writeVec[0].iov_len = 4;
+    numVecs = 1;
+
+    if (msg->mLength > 0) {
+        assert(msg->mData != NULL);
+        writeVec[1].iov_base = msg->mData;
+        writeVec[1].iov_len = msg->mLength;
+        numVecs++;
+    }
+
+    /* write it all in one shot; not worrying about partial writes for now */
+    actual = _ws_writev(fd, writeVec, numVecs);
+    if (actual != len+2) {
+        wsLog("failed writing message to fd %d: %d of %d %s\n",
+            fd, actual, len+2, strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Release storage associated with a Message.
+ */
+static void Message_release(Message* msg)
+{
+    free(msg->mData);
+    msg->mData = NULL;
+}
+
+/*
+ * Extract a name/value pair from a message.
+ */
+static int getConfig(const Message* msg, const char** name, const char** val)
+{
+    if (msg->mLength < 2) {
+        wsLog("message len (%d) is too short\n", msg->mLength);
+        return -1;
+    }
+    const char* ptr = (const char*) msg->mData;
+
+    *name = (const char*) ptr;
+    *val = (const char*) (ptr + strlen((char*)ptr) +1);
+    return 0;
+}
+
+/*
+ * Extract a command from a message.
+ */
+static int getCommand(const Message* msg, int* pCmd, int* pArg)
+{
+    if (msg->mLength != sizeof(int) * 2) {
+        wsLog("message len (%d) is wrong for cmd (%d)\n",
+            msg->mLength, sizeof(int) * 2);
+        return -1;
+    }
+
+    /* this assumes 32-bit alignment on mData */
+    const int* ptr = (const int*) msg->mData;
+
+    *pCmd = ptr[0];
+    *pArg = ptr[1];
+
+    return 0;
+}
+
+/*
+ * Extract an extended command from a message.
+ */
+static int getCommandExt(const Message* msg, int* pCmd, int* pArg0,
+    int* pArg1, int* pArg2)
+{
+    if (msg->mLength != sizeof(int) * 4) {
+        wsLog("message len (%d) is wrong for cmd (%d)\n",
+            msg->mLength, sizeof(int) * 4);
+        return -1;
+    }
+
+    /* this assumes 32-bit alignment on mData */
+    const int* ptr = (const int*) msg->mData;
+
+    *pCmd = ptr[0];
+    *pArg0 = ptr[1];
+    *pArg1 = ptr[2];
+    *pArg2 = ptr[3];
+
+    return 0;
+}
+
+/*
+ * Attach 8 bytes of data with "cmd" and "arg" to "msg".
+ *
+ * "msg->mData" will need to be freed by the caller.  (This approach made
+ * more sense when C++ destructors were available, but it's just not worth
+ * reworking it.)
+ */
+static int setCommand(Message* msg, int cmd, int arg)
+{
+    Message_clear(msg);
+
+    msg->mLength = 8;
+    msg->mData = malloc(msg->mLength);
+    msg->mType = kTypeCommand;
+
+    /* assumes 32-bit alignment on malloc blocks */
+    int* pInt = (int*) msg->mData;
+    pInt[0] = cmd;
+    pInt[1] = arg;
+
+    return 0;
+}
+
+/*
+ * Construct the full path.  The caller must free() the return value.
+ */
+static char* makeFilename(const char* name)
+{
+    static const char* kBasePath = "/tmp/android-";
+    char* fileName;
+
+    assert(name != NULL && name[0] != '\0');
+
+    fileName = (char*) malloc(strlen(kBasePath) + strlen(name) + 1);
+    strcpy(fileName, kBasePath);
+    strcat(fileName, name);
+
+    return fileName;
+}
+
+/*
+ * Attach to a SysV shared memory segment.
+ */
+static int attachToShmem(int key, int* pShmid, void** pAddr, long* pLength)
+{
+    int shmid;
+
+    shmid = shmget(key, 0, 0);
+    if (shmid == -1) {
+        wsLog("ERROR: failed to find shmem key=%d\n", key);
+        return -1;
+    }
+
+    void* addr = shmat(shmid, NULL, 0);
+    if (addr == (void*) -1) {
+        wsLog("ERROR: could not attach to key=%d shmid=%d\n", key, shmid);
+        return -1;
+    }
+
+    struct shmid_ds shmids;
+    int cc;
+
+    cc = shmctl(shmid, IPC_STAT, &shmids);
+    if (cc != 0) {
+        wsLog("ERROR: could not IPC_STAT shmid=%d\n", shmid);
+        return -1;
+    }
+    *pLength = shmids.shm_segsz;
+
+    *pAddr = addr;
+    *pShmid = shmid;
+    return 0;
+}
+
+/*
+ * Attach to a SysV semaphore.
+ */
+static int attachToSem(int key, int* pSemid)
+{
+    int semid;
+
+    semid = semget(key, 0, 0);
+    if (semid == -1) {
+        wsLog("ERROR: failed to attach to semaphore key=%d\n", key);
+        return -1;
+    }
+
+    *pSemid = semid;
+    return 0;
+}
+
+/*
+ * "Adjust" a semaphore.
+ */
+static int adjustSem(int semid, int adj)
+{
+    const int wait = 1;
+    struct sembuf op;
+    int cc;
+
+    op.sem_num = 0;
+    op.sem_op = adj;
+    op.sem_flg = SEM_UNDO;
+    if (!wait)
+        op.sem_flg |= IPC_NOWAIT;
+
+    cc = semop(semid, &op, 1);
+    if (cc != 0) {
+        if (wait || errno != EAGAIN) {
+            wsLog("Warning:"
+                " semaphore adjust by %d failed for semid=%d (errno=%d)\n",
+                adj, semid, errno);
+        }
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Acquire the semaphore associated with a display.
+ */
+void wsLockDisplay(int displayIdx)
+{
+    assert(displayIdx >= 0 && displayIdx < gWrapSim.numDisplays);
+    int semid = gWrapSim.display[displayIdx].semid;
+
+    (void) adjustSem(semid, -1);
+}
+
+/*
+ * Acquire the semaphore associated with a display.
+ */
+void wsUnlockDisplay(int displayIdx)
+{
+    assert(displayIdx >= 0 && displayIdx < gWrapSim.numDisplays);
+    int semid = gWrapSim.display[displayIdx].semid;
+
+    (void) adjustSem(semid, 1);
+}
+
+/*
+ * Process the display config from the simulator
+ *
+ * Right now this is a blob of raw data that looks like this:
+ *  +00 magic number
+ *  +04 #of displays
+ *  +08 display 0:
+ *      +00 width
+ *      +04 height
+ *      +08 format
+ *      +0c refresh rate
+ *      +10 shmem key
+ *  +1c display 1...
+ */
+static int handleDisplayConfig(const int* pData, int length)
+{
+    int numDisplays;
+
+    if (length < 8) {
+        wsLog("Bad display config: length is %d\n", length);
+        return -1;
+    }
+    assert(*pData == kDisplayConfigMagic);
+
+    /*
+     * Pull out the #of displays.  If it looks valid, configure the runtime.
+     */
+    pData++;        // skip over magic
+    numDisplays = *pData++;
+
+    if (numDisplays <= 0 || numDisplays > kMaxDisplays) {
+        wsLog("Bizarre display count %d\n", numDisplays);
+        return -1;
+    }
+    if (length != 8 + numDisplays * kValuesPerDisplay * (int)sizeof(int)) {
+        wsLog("Bad display config: length is %d (expected %d)\n",
+            length, 8 + numDisplays * kValuesPerDisplay * (int)sizeof(int));
+        return -1;
+    }
+
+    /*
+     * Extract the config values.
+     *
+     * The runtime doesn't support multiple devices, so we don't either.
+     */
+    int i;
+    for (i = 0; i < numDisplays; i++) {
+        gWrapSim.display[i].width = pData[0];
+        gWrapSim.display[i].height = pData[1];
+        gWrapSim.display[i].shmemKey = pData[4];
+        /* format/refresh no longer needed */
+
+        void* addr;
+        int shmid, semid;
+        long length;
+        if (attachToShmem(gWrapSim.display[i].shmemKey, &shmid, &addr,
+                &length) != 0)
+        {
+            wsLog("Unable to connect to shared memory\n");
+            return -1;
+        }
+
+        if (attachToSem(gWrapSim.display[i].shmemKey, &semid) != 0) {
+            wsLog("Unable to attach to sempahore\n");
+            return -1;
+        }
+
+        gWrapSim.display[i].shmid = shmid;
+        gWrapSim.display[i].addr = addr;
+        gWrapSim.display[i].length = length;
+        gWrapSim.display[i].semid = semid;
+
+        wsLog("Display %d: width=%d height=%d\n",
+            i,
+            gWrapSim.display[i].width,
+            gWrapSim.display[i].height);
+        wsLog("  shmem=0x%08x addr=%p len=%ld semid=%d\n",
+            gWrapSim.display[i].shmemKey,
+            gWrapSim.display[i].addr,
+            gWrapSim.display[i].length,
+            gWrapSim.display[i].semid);
+
+        pData += kValuesPerDisplay;
+    }
+    gWrapSim.numDisplays = numDisplays;
+
+    return 0;
+}
+
+
+/*
+ * Initialize our connection to the simulator, which will be listening on
+ * a UNIX domain socket.
+ *
+ * On success, this configures gWrapSim.simulatorFd and returns 0.
+ */
+static int openSimConnection(const char* name)
+{
+    int result = -1;
+    char* fileName = NULL;
+    int sock = -1;
+    int cc;
+
+    assert(gWrapSim.simulatorFd == -1);
+
+    fileName = makeFilename(name);
+
+    struct sockaddr_un addr;
+    
+    sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (sock < 0) {
+        wsLog("UNIX domain socket create failed (errno=%d)\n", errno);
+        goto bail;
+    }
+
+    /* connect to socket; fails if file doesn't exist */
+    strcpy(addr.sun_path, fileName);    // max 108 bytes
+    addr.sun_family = AF_UNIX;
+    cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
+    if (cc < 0) {
+        // ENOENT means socket file doesn't exist
+        // ECONNREFUSED means socket exists but nobody is listening
+        wsLog("AF_UNIX connect failed for '%s': %s\n",
+            fileName, strerror(errno));
+        goto bail;
+    }
+
+    gWrapSim.simulatorFd = sock;
+    sock = -1;
+
+    result = 0;
+    wsLog("+++ connected to '%s'\n", fileName);
+
+bail:
+    if (sock >= 0)
+        _ws_close(sock);
+    free(fileName);
+    return result;
+}
+
+/*
+ * Prepare communication with the front end.  We wait for a "hello" from
+ * the other side, and respond in kind.
+ */
+static int prepSimConnection(void)
+{
+    /* NOTE: this is endian-specific; we're x86 Linux only, so no problem */
+    static const unsigned int hello = kHelloMsg;
+    static const unsigned int helloAck = kHelloAckMsg;
+    Message msg;
+
+    if (Message_read(&msg, gWrapSim.simulatorFd) != 0) {
+        wsLog("hello read failed\n");
+        return -1;
+    }
+
+    if (memcmp(msg.mData, &hello, 4) != 0) {
+        wsLog("Got bad hello from peer\n");
+        return -1;
+    }
+
+    Message_release(&msg);
+
+    msg.mType = kTypeRaw;
+    msg.mData = (unsigned char*) &helloAck;
+    msg.mLength = 4;
+
+    if (Message_write(&msg, gWrapSim.simulatorFd) != 0) {
+        wsLog("hello ack write failed\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Get the sim front-end configuration.  We loop here until the sim claims
+ * to be done with us.
+ */
+static int getSimConfig(void)
+{
+    Message msg;
+    int joinNewGroup, grabTerminal, done;
+    int result = -1;
+
+    joinNewGroup = grabTerminal = done = 0;
+    Message_clear(&msg);        // clear out msg->mData
+
+    wsLog("+++ awaiting hardware configuration\n");
+    while (!done) {
+        if (Message_read(&msg, gWrapSim.simulatorFd) != 0) {
+            wsLog("failed receiving config from parent\n");
+            goto bail;
+        }
+
+        if (msg.mType == kTypeCommand) {
+            int cmd, arg;
+
+            if (getCommand(&msg, &cmd, &arg) != 0)
+                goto bail;
+
+            switch (cmd) {
+            case kCommandGoAway:
+                wsLog("Simulator front-end is busy\n");
+                goto bail;
+            case kCommandNewPGroup:
+                joinNewGroup = 1;
+                grabTerminal = (arg != 0);
+                wsLog("Simulator wants us to be in a new pgrp (term=%d)\n",
+                    grabTerminal);
+                break;
+            case kCommandConfigDone:
+                done = 1;
+                break;
+            default:
+                wsLog("Got unexpected command %d/%d\n", cmd, arg);
+                break;
+            }
+        } else if (msg.mType == kTypeRaw) {
+            /* assumes 32-bit alignment and identical byte ordering */
+            int* pData = (int*) msg.mData;
+            if (msg.mLength >= 4 && *pData == kDisplayConfigMagic) {
+                if (handleDisplayConfig(pData, msg.mLength) != 0)
+                    goto bail;
+            }
+        } else if (msg.mType == kTypeConfig) {
+            const char *name;
+            const char *val;
+            getConfig(&msg, &name, &val);
+            if(strcmp(name, "keycharmap") == 0) {
+                free((void*)gWrapSim.keyMap);
+                gWrapSim.keyMap = strdup(val);
+            }
+        } else {
+            wsLog("Unexpected msg type %d during startup\n", msg.mType);
+            goto bail;
+        }
+
+        /* clear out the data field if necessary */
+        Message_release(&msg);
+    }
+
+    wsLog("Configuration received from simulator\n");
+
+    if (joinNewGroup) {
+        /* set pgid to pid */
+        pid_t pgid = getpid();
+        setpgid(0, pgid);
+
+        /*
+         * Put our pgrp in the foreground.
+         * tcsetpgrp() from background process causes us to get a SIGTTOU,
+         * which is mostly harmless but makes tcsetpgrp() fail with EINTR.
+         */
+        signal(SIGTTOU, SIG_IGN);
+        if (grabTerminal) {
+            if (tcsetpgrp(fileno(stdin), getpgrp()) != 0) {
+                wsLog("tcsetpgrp(%d, %d) failed (errno=%d)\n",
+                    fileno(stdin), getpgrp(), errno);
+            }
+            wsLog("Set pgrp %d as foreground\n", (int) getpgrp());
+        }
+    
+        /* tell the sim where we're at */
+        Message msg;
+        setCommand(&msg, kCommandNewPGroupCreated, pgid);
+        Message_write(&msg, gWrapSim.simulatorFd);
+        Message_release(&msg);
+    }
+
+    result = 0;
+
+bail:
+    /* make sure the data was freed */
+    Message_release(&msg);
+    //wsLog("bailing, result=%d\n", result);
+    return result;
+}
+
+/*
+ * Connect to the simulator and exchange pleasantries.
+ *
+ * Returns 0 on success.
+ */
+static int connectToSim(void)
+{
+    if (openSimConnection(kAndroidPipeName) != 0)
+        return -1;
+
+    if (prepSimConnection() != 0)
+        return -1;
+
+    if (getSimConfig() != 0)
+        return -1;
+
+    wsLog("+++ sim is ready to go\n");
+
+    return 0;
+}
+
+/*
+ * Listen to the sim forever or until the front end shuts down, whichever
+ * comes first.
+ *
+ * All we're really getting here are key events.
+ */
+static void listenToSim(void)
+{
+    wsLog("--- listening for input events from front end\n");
+
+    while (1) {
+        Message msg;
+
+        Message_clear(&msg);
+        if (Message_read(&msg, gWrapSim.simulatorFd) != 0) {
+            wsLog("--- sim message read failed\n");
+            return;
+        }
+
+        if (msg.mType == kTypeCommand) {
+            int cmd, arg;
+
+            if (getCommand(&msg, &cmd, &arg) != 0) {
+                wsLog("bad command from sim?\n");
+                continue;
+            }
+
+            switch (cmd) {
+            case kCommandQuit:
+                wsLog("--- sim sent us a QUIT message\n");
+                return;
+            case kCommandKeyDown:
+                wsLog("KEY DOWN: %d\n", arg);
+                wsSendSimKeyEvent(arg, 1);
+                break;
+            case kCommandKeyUp:
+                wsLog("KEY UP: %d\n", arg);
+                wsSendSimKeyEvent(arg, 0);
+                break;
+            default:
+                wsLog("--- sim sent unrecognized command %d\n", cmd);
+                break;
+            }
+
+            Message_release(&msg);
+        } else if (msg.mType == kTypeCommandExt) {
+            int cmd, arg0, arg1, arg2;
+
+            if (getCommandExt(&msg, &cmd, &arg0, &arg1, &arg2) != 0) {
+                wsLog("bad ext-command from sim?\n");
+                continue;
+            }
+
+            switch (cmd) {
+            case kCommandTouch:
+                wsSendSimTouchEvent(arg0, arg1, arg2);
+                break;
+            }
+
+            Message_release(&msg);
+        } else {
+            wsLog("--- sim sent non-command message, type=%d\n", msg.mType);
+        }
+    }
+
+    assert(0);      // not reached
+}
+
+
+/*
+ * Tell the simulator front-end that the display has been updated.
+ */
+void wsPostDisplayUpdate(int displayIdx)
+{
+    if (gWrapSim.simulatorFd < 0) {
+        wsLog("Not posting display update -- sim not ready\n");
+        return;
+    }
+
+    Message msg;
+
+    setCommand(&msg, kCommandUpdateDisplay, displayIdx);
+    Message_write(&msg, gWrapSim.simulatorFd);
+    Message_release(&msg);
+}
+
+/*
+ * Send a log message to the front-end.
+ */
+void wsPostLogMessage(int logPrio, const char* tag, const char* message)
+{
+    if (gWrapSim.simulatorFd < 0) {
+        wsLog("Not posting log message -- sim not ready\n");
+        return;
+    }
+
+    time_t when = time(NULL);
+    int pid = (int) getpid();
+    int tagLen, messageLen, totalLen;
+
+    tagLen = strlen(tag) +1;
+    messageLen = strlen(message) +1;
+    totalLen = sizeof(int) * 3 + tagLen + messageLen;
+    unsigned char outBuf[totalLen];
+    unsigned char* cp = outBuf;
+
+    /* See Message::set/getLogBundle() in simulator/MessageStream.cpp. */
+    memcpy(cp, &when, sizeof(int));
+    cp += sizeof(int);
+    memcpy(cp, &logPrio, sizeof(int));
+    cp += sizeof(int);
+    memcpy(cp, &pid, sizeof(int));
+    cp += sizeof(int);
+    memcpy(cp, tag, tagLen);
+    cp += tagLen;
+    memcpy(cp, message, messageLen);
+    cp += messageLen;
+
+    assert(cp - outBuf == totalLen);
+
+    Message msg;
+    msg.mType = kTypeLogBundle;
+    msg.mData = outBuf;
+    msg.mLength = totalLen;
+    Message_write(&msg, gWrapSim.simulatorFd);
+
+    msg.mData = NULL;       // don't free
+    Message_release(&msg);
+}
+
+/*
+ * Turn the vibrating notification device on or off.
+ */
+void wsEnableVibration(int vibrateOn)
+{
+    if (gWrapSim.simulatorFd < 0) {
+        wsLog("Not posting vibrator update -- sim not ready\n");
+        return;
+    }
+
+    Message msg;
+
+    //wsLog("+++ sending vibrate:%d\n", vibrateOn);
+
+    setCommand(&msg, kCommandVibrate, vibrateOn);
+    Message_write(&msg, gWrapSim.simulatorFd);
+    Message_release(&msg);
+}
+
diff --git a/simulator/wrapsim/SimMgr.h b/simulator/wrapsim/SimMgr.h
new file mode 100644
index 0000000..378dfbc
--- /dev/null
+++ b/simulator/wrapsim/SimMgr.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Simulator interactions.
+ */
+#ifndef _WRAPSIM_SIMULATOR_H
+#define _WRAPSIM_SIMULATOR_H
+
+/*
+ * Commands exchanged between simulator and runtime.
+ *
+ * NOTE: this is cloned from SimRuntime.h -- fix this.
+ */
+typedef enum SimCommand {
+    kCommandUnknown = 0,
+
+    /* sent from sim to runtime */
+    kCommandGoAway,             // sim says: go away, I'm busy
+    kCommandConfigDone,         // sim says: done sending config
+    kCommandQuit,               // quit nicely
+    kCommandNewPGroup,          // process group management
+    kCommandKeyDown,            // key has been pressed
+    kCommandKeyUp,              // key has been released
+    kCommandTouch,              // finger touched/lifted/dragged
+
+    /* sent from runtime to sim */
+    kCommandNewPGroupCreated,   // send process group as argument
+    kCommandRuntimeReady,       // we're initialized and about to start
+    kCommandUpdateDisplay,      // display has been updated
+    kCommandVibrate,            // vibrate on or off
+} SimCommand;
+
+/*
+ * Touch screen action; also clined from SimRuntime.h.
+ */
+typedef enum TouchMode {
+    kTouchDown = 0,
+    kTouchUp = 1,
+    kTouchDrag = 2
+} TouchMode;
+
+
+/*
+ * Some parameters for config exchange.
+ */
+enum {
+    kDisplayConfigMagic = 0x44495350,
+    kValuesPerDisplay = 5,
+};
+
+/*
+ * UNIX domain socket name.
+ */
+#define kAndroidPipeName        "runtime"
+
+int wsSimConnect(void);
+
+/*
+ * Display management.
+ */
+void wsLockDisplay(int displayIdx);
+void wsUnlockDisplay(int displayIdx);
+void wsPostDisplayUpdate(int displayIdx);
+
+/*
+ * Send a log message.
+ */
+void wsPostLogMessage(int logPrio, const char* tag, const char* msg);
+
+/*
+ * Change the state of the vibration device.
+ */
+void wsEnableVibration(int vibrateOn);
+
+#endif /*_WRAPSIM_SIMULATOR_H*/