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