blob: c387d3a267bdc13dc8ec95790b972fdc130f21f2 [file] [log] [blame]
//
// 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(int32_t 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