Pulled a fix to the simulator app ("long" is 64-bit when 32-bit was
needed) from master. sim-eng is useful for verifying the x86 SMP stuff.
Also quieted a compiler warning.
Change-Id: I399ea4923b7e0650d5f995e056c751374b057fbd
989 lines
24 KiB
C
989 lines
24 KiB
C
/*
|
|
* 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 = NULL;
|
|
const char* val = NULL;
|
|
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);
|
|
}
|
|
|