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*/