The Android Open Source Project | 52d4c30 | 2009-03-03 19:29:09 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2007 The Android Open Source Project |
| 3 | * |
| 4 | * Simulator interactions. |
| 5 | * |
| 6 | * TODO: for multi-process we probably need to switch to a new process |
| 7 | * group if we are the first process (could be runtime, could be gdb), |
| 8 | * rather than wait for the simulator to tell us to switch. |
| 9 | */ |
| 10 | #include "Common.h" |
| 11 | |
| 12 | #include <stdlib.h> |
| 13 | #include <stdio.h> |
| 14 | #include <stdint.h> |
| 15 | #include <sys/types.h> |
| 16 | #include <sys/socket.h> |
| 17 | #include <sys/ipc.h> |
| 18 | #include <sys/shm.h> |
| 19 | #include <sys/sem.h> |
| 20 | #include <sys/un.h> |
| 21 | #include <signal.h> |
| 22 | #include <assert.h> |
| 23 | |
| 24 | // fwd |
| 25 | static int connectToSim(void); |
| 26 | static void listenToSim(void); |
| 27 | |
| 28 | /* |
| 29 | * Env var to restrict who tries to talk to the front end. |
| 30 | */ |
| 31 | #define kWrapSimConnectedEnv "WRAP_SIM_CONNECTED" |
| 32 | |
| 33 | |
| 34 | /* |
| 35 | * Signal the main thread that we're ready to continue. |
| 36 | */ |
| 37 | static void signalMainThread(void) |
| 38 | { |
| 39 | int cc; |
| 40 | |
| 41 | cc = pthread_mutex_lock(&gWrapSim.startLock); |
| 42 | assert(cc == 0); |
| 43 | |
| 44 | gWrapSim.startReady = 1; |
| 45 | |
| 46 | cc = pthread_cond_signal(&gWrapSim.startCond); |
| 47 | assert(cc == 0); |
| 48 | |
| 49 | cc = pthread_mutex_unlock(&gWrapSim.startLock); |
| 50 | assert(cc == 0); |
| 51 | } |
| 52 | |
| 53 | |
| 54 | /* |
| 55 | * Entry point for the sim management thread. |
| 56 | * |
| 57 | * Once we have established a connection to the simulator and are ready |
| 58 | * for other threads to send messages, we signal the main thread. |
| 59 | */ |
| 60 | static void* simThreadEntry(void* arg) |
| 61 | { |
| 62 | wsLog("--- sim manager thread started\n"); |
| 63 | |
| 64 | /* |
| 65 | * Establish a connection to the simulator front-end. If we can't do |
| 66 | * that, we have no access to input or output devices, and we might |
| 67 | * as well give up. |
| 68 | */ |
| 69 | if (connectToSim() != 0) { |
| 70 | signalMainThread(); |
| 71 | return NULL; |
| 72 | } |
| 73 | |
| 74 | /* success! */ |
| 75 | wsLog("--- sim manager thread ready\n"); |
| 76 | gWrapSim.simulatorInitFailed = 0; |
| 77 | signalMainThread(); |
| 78 | |
| 79 | listenToSim(); |
| 80 | |
| 81 | wsLog("--- sim manager thread exiting\n"); |
| 82 | |
| 83 | return NULL; |
| 84 | } |
| 85 | |
| 86 | /* |
| 87 | * If we think we're not yet connected to the sim, do so now. We only |
| 88 | * want to do this once per process *group*, so we control access with |
| 89 | * an environment variable. |
| 90 | */ |
| 91 | int wsSimConnect(void) |
| 92 | { |
| 93 | /* |
| 94 | * If the environment variable hasn't been set, assume we're the first |
| 95 | * to get here, and should attach to the simulator. We set the env |
| 96 | * var here whether or not we succeed in connecting to the sim. |
| 97 | * |
| 98 | * (For correctness we should wrap the getenv/setenv in a semaphore.) |
| 99 | */ |
| 100 | if (getenv(kWrapSimConnectedEnv) == NULL) { |
| 101 | pthread_attr_t threadAttr; |
| 102 | pthread_t threadHandle; |
| 103 | int cc; |
| 104 | |
| 105 | gWrapSim.simulatorInitFailed = 1; |
| 106 | setenv(kWrapSimConnectedEnv, "1", 1); |
| 107 | |
| 108 | cc = pthread_mutex_lock(&gWrapSim.startLock); |
| 109 | assert(cc == 0); |
| 110 | |
| 111 | pthread_attr_init(&threadAttr); |
| 112 | pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED); |
| 113 | cc = pthread_create(&threadHandle, &threadAttr, simThreadEntry, NULL); |
| 114 | if (cc != 0) { |
| 115 | wsLog("Unable to create new thread: %s\n", strerror(errno)); |
| 116 | abort(); |
| 117 | } |
| 118 | |
| 119 | while (!gWrapSim.startReady) { |
| 120 | cc = pthread_cond_wait(&gWrapSim.startCond, &gWrapSim.startLock); |
| 121 | assert(cc == 0); |
| 122 | } |
| 123 | |
| 124 | cc = pthread_mutex_unlock(&gWrapSim.startLock); |
| 125 | assert(cc == 0); |
| 126 | |
| 127 | if (gWrapSim.simulatorInitFailed) { |
| 128 | wsLog("Simulator initialization failed, bailing\n"); |
| 129 | |
| 130 | /* this *should* be okay to do */ |
| 131 | fprintf(stderr, "Fatal error:" |
| 132 | " unable to connect to sim front-end (not running?)\n"); |
| 133 | abort(); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | wsLog("+++ continuing\n"); |
| 138 | return 0; |
| 139 | } |
| 140 | |
| 141 | |
| 142 | /* |
| 143 | * =========================================================================== |
| 144 | * Message / MessageStream |
| 145 | * =========================================================================== |
| 146 | */ |
| 147 | |
| 148 | /* |
| 149 | * This is a quick & dirty rewrite of the C++ Message and MessageStream |
| 150 | * classes, ported to C, reduced in generality, with syscalls stubbed |
| 151 | * where necessary. I didn't fix the API to make it more sensible in C |
| 152 | * (which lacks destructors), so some of this is a little fragile or |
| 153 | * awkward. |
| 154 | */ |
| 155 | |
| 156 | /* values for message type byte; must match android::Message constants */ |
| 157 | typedef enum MessageType { |
| 158 | kTypeUnknown = 0, |
| 159 | kTypeRaw, // chunk of raw data |
| 160 | kTypeConfig, // send a name=value pair to peer |
| 161 | kTypeCommand, // simple command w/arg |
| 162 | kTypeCommandExt, // slightly more complicated command |
| 163 | kTypeLogBundle, // multi-part log message |
| 164 | } MessageType; |
| 165 | |
| 166 | /* |
| 167 | * Reusable message object. |
| 168 | */ |
| 169 | typedef struct Message { |
| 170 | MessageType mType; |
| 171 | unsigned char* mData; |
| 172 | int mLength; |
| 173 | } Message; |
| 174 | |
| 175 | /* magic init messages; must match android::MessageStream constants */ |
| 176 | enum { |
| 177 | kHelloMsg = 0x4e303047, // 'N00G' |
| 178 | kHelloAckMsg = 0x31455221, // '1ER!' |
| 179 | }; |
| 180 | |
| 181 | |
| 182 | /* |
| 183 | * Clear out a Message. |
| 184 | */ |
| 185 | static void Message_clear(Message* msg) |
| 186 | { |
| 187 | memset(msg, 0, sizeof(Message)); |
| 188 | } |
| 189 | |
| 190 | /* |
| 191 | * Keep reading until we get all bytes or hit EOF/error. "fd" is expected |
| 192 | * to be in blocking mode. |
| 193 | * |
| 194 | * Returns 0 on success. |
| 195 | */ |
| 196 | static int readAll(int fd, void* buf, size_t count) |
| 197 | { |
| 198 | ssize_t actual; |
| 199 | ssize_t have; |
| 200 | |
| 201 | have = 0; |
| 202 | while (have != (ssize_t) count) { |
| 203 | actual = _ws_read(fd, ((char*) buf) + have, count - have); |
| 204 | if (actual < 0) { |
| 205 | if (errno == EINTR) |
| 206 | continue; |
| 207 | wsLog("read %d failed: %s\n", fd, strerror(errno)); |
| 208 | } else if (actual == 0) { |
| 209 | wsLog("early EOF on %d\n", fd); |
| 210 | return -1; |
| 211 | } else { |
| 212 | have += actual; |
| 213 | } |
| 214 | |
| 215 | assert(have <= (ssize_t)count); |
| 216 | } |
| 217 | |
| 218 | return 0; |
| 219 | } |
| 220 | |
| 221 | #if 0 |
| 222 | /* |
| 223 | * Keep writing until we put all bytes or hit an error. "fd" is expected |
| 224 | * to be in blocking mode. |
| 225 | * |
| 226 | * Returns 0 on success. |
| 227 | */ |
| 228 | static int writeAll(int fd, const void* buf, size_t count) |
| 229 | { |
| 230 | ssize_t actual; |
| 231 | ssize_t have; |
| 232 | |
| 233 | have = 0; |
| 234 | while (have != count) { |
| 235 | actual = _ws_write(fd, ((const char*) buf) + have, count - have); |
| 236 | if (actual < 0) { |
| 237 | if (errno == EINTR) |
| 238 | continue; |
| 239 | wsLog("write %d failed: %s\n", fd, strerror(errno)); |
| 240 | } else if (actual == 0) { |
| 241 | wsLog("wrote zero on %d\n", fd); |
| 242 | return -1; |
| 243 | } else { |
| 244 | have += actual; |
| 245 | } |
| 246 | |
| 247 | assert(have <= count); |
| 248 | } |
| 249 | |
| 250 | return 0; |
| 251 | } |
| 252 | #endif |
| 253 | |
| 254 | /* |
| 255 | * Read a message from the specified file descriptor. |
| 256 | * |
| 257 | * The caller must Message_release(&msg). |
| 258 | * |
| 259 | * We guarantee 32-bit alignment for msg->mData. |
| 260 | */ |
| 261 | static int Message_read(Message* msg, int fd) |
| 262 | { |
| 263 | unsigned char header[4]; |
| 264 | |
| 265 | readAll(fd, header, 4); |
| 266 | |
| 267 | msg->mType = (MessageType) header[2]; |
| 268 | msg->mLength = header[0] | header[1] << 8; |
| 269 | msg->mLength -= 2; // we already read two of them in the header |
| 270 | |
| 271 | if (msg->mLength > 0) { |
| 272 | int actual; |
| 273 | |
| 274 | /* Linux malloc guarantees at least 32-bit alignment */ |
| 275 | msg->mData = (unsigned char*) malloc(msg->mLength); |
| 276 | if (msg->mData == NULL) { |
| 277 | wsLog("alloc %d failed\n", msg->mLength); |
| 278 | return -1; |
| 279 | } |
| 280 | |
| 281 | if (readAll(fd, msg->mData, msg->mLength) != 0) { |
| 282 | wsLog("failed reading message body (wanted %d)\n", msg->mLength); |
| 283 | return -1; |
| 284 | } |
| 285 | } else { |
| 286 | msg->mData = NULL; |
| 287 | } |
| 288 | |
| 289 | return 0; |
| 290 | } |
| 291 | |
| 292 | /* |
| 293 | * Write a message to the specified file descriptor. |
| 294 | * |
| 295 | * The caller must Message_release(&msg). |
| 296 | */ |
| 297 | static int Message_write(Message* msg, int fd) |
| 298 | { |
| 299 | struct iovec writeVec[2]; |
| 300 | unsigned char header[4]; |
| 301 | int len, numVecs; |
| 302 | ssize_t actual; |
| 303 | |
| 304 | len = msg->mLength + 2; |
| 305 | header[0] = len & 0xff; |
| 306 | header[1] = (len >> 8) & 0xff; |
| 307 | header[2] = msg->mType; |
| 308 | header[3] = 0; |
| 309 | writeVec[0].iov_base = header; |
| 310 | writeVec[0].iov_len = 4; |
| 311 | numVecs = 1; |
| 312 | |
| 313 | if (msg->mLength > 0) { |
| 314 | assert(msg->mData != NULL); |
| 315 | writeVec[1].iov_base = msg->mData; |
| 316 | writeVec[1].iov_len = msg->mLength; |
| 317 | numVecs++; |
| 318 | } |
| 319 | |
| 320 | /* write it all in one shot; not worrying about partial writes for now */ |
| 321 | actual = _ws_writev(fd, writeVec, numVecs); |
| 322 | if (actual != len+2) { |
| 323 | wsLog("failed writing message to fd %d: %d of %d %s\n", |
| 324 | fd, actual, len+2, strerror(errno)); |
| 325 | return -1; |
| 326 | } |
| 327 | |
| 328 | return 0; |
| 329 | } |
| 330 | |
| 331 | /* |
| 332 | * Release storage associated with a Message. |
| 333 | */ |
| 334 | static void Message_release(Message* msg) |
| 335 | { |
| 336 | free(msg->mData); |
| 337 | msg->mData = NULL; |
| 338 | } |
| 339 | |
| 340 | /* |
| 341 | * Extract a name/value pair from a message. |
| 342 | */ |
| 343 | static int getConfig(const Message* msg, const char** name, const char** val) |
| 344 | { |
| 345 | if (msg->mLength < 2) { |
| 346 | wsLog("message len (%d) is too short\n", msg->mLength); |
| 347 | return -1; |
| 348 | } |
| 349 | const char* ptr = (const char*) msg->mData; |
| 350 | |
| 351 | *name = (const char*) ptr; |
| 352 | *val = (const char*) (ptr + strlen((char*)ptr) +1); |
| 353 | return 0; |
| 354 | } |
| 355 | |
| 356 | /* |
| 357 | * Extract a command from a message. |
| 358 | */ |
| 359 | static int getCommand(const Message* msg, int* pCmd, int* pArg) |
| 360 | { |
| 361 | if (msg->mLength != sizeof(int) * 2) { |
| 362 | wsLog("message len (%d) is wrong for cmd (%d)\n", |
| 363 | msg->mLength, sizeof(int) * 2); |
| 364 | return -1; |
| 365 | } |
| 366 | |
| 367 | /* this assumes 32-bit alignment on mData */ |
| 368 | const int* ptr = (const int*) msg->mData; |
| 369 | |
| 370 | *pCmd = ptr[0]; |
| 371 | *pArg = ptr[1]; |
| 372 | |
| 373 | return 0; |
| 374 | } |
| 375 | |
| 376 | /* |
| 377 | * Extract an extended command from a message. |
| 378 | */ |
| 379 | static int getCommandExt(const Message* msg, int* pCmd, int* pArg0, |
| 380 | int* pArg1, int* pArg2) |
| 381 | { |
| 382 | if (msg->mLength != sizeof(int) * 4) { |
| 383 | wsLog("message len (%d) is wrong for cmd (%d)\n", |
| 384 | msg->mLength, sizeof(int) * 4); |
| 385 | return -1; |
| 386 | } |
| 387 | |
| 388 | /* this assumes 32-bit alignment on mData */ |
| 389 | const int* ptr = (const int*) msg->mData; |
| 390 | |
| 391 | *pCmd = ptr[0]; |
| 392 | *pArg0 = ptr[1]; |
| 393 | *pArg1 = ptr[2]; |
| 394 | *pArg2 = ptr[3]; |
| 395 | |
| 396 | return 0; |
| 397 | } |
| 398 | |
| 399 | /* |
| 400 | * Attach 8 bytes of data with "cmd" and "arg" to "msg". |
| 401 | * |
| 402 | * "msg->mData" will need to be freed by the caller. (This approach made |
| 403 | * more sense when C++ destructors were available, but it's just not worth |
| 404 | * reworking it.) |
| 405 | */ |
| 406 | static int setCommand(Message* msg, int cmd, int arg) |
| 407 | { |
| 408 | Message_clear(msg); |
| 409 | |
| 410 | msg->mLength = 8; |
| 411 | msg->mData = malloc(msg->mLength); |
| 412 | msg->mType = kTypeCommand; |
| 413 | |
| 414 | /* assumes 32-bit alignment on malloc blocks */ |
| 415 | int* pInt = (int*) msg->mData; |
| 416 | pInt[0] = cmd; |
| 417 | pInt[1] = arg; |
| 418 | |
| 419 | return 0; |
| 420 | } |
| 421 | |
| 422 | /* |
| 423 | * Construct the full path. The caller must free() the return value. |
| 424 | */ |
| 425 | static char* makeFilename(const char* name) |
| 426 | { |
| 427 | static const char* kBasePath = "/tmp/android-"; |
| 428 | char* fileName; |
| 429 | |
| 430 | assert(name != NULL && name[0] != '\0'); |
| 431 | |
| 432 | fileName = (char*) malloc(strlen(kBasePath) + strlen(name) + 1); |
| 433 | strcpy(fileName, kBasePath); |
| 434 | strcat(fileName, name); |
| 435 | |
| 436 | return fileName; |
| 437 | } |
| 438 | |
| 439 | /* |
| 440 | * Attach to a SysV shared memory segment. |
| 441 | */ |
| 442 | static int attachToShmem(int key, int* pShmid, void** pAddr, long* pLength) |
| 443 | { |
| 444 | int shmid; |
| 445 | |
| 446 | shmid = shmget(key, 0, 0); |
| 447 | if (shmid == -1) { |
| 448 | wsLog("ERROR: failed to find shmem key=%d\n", key); |
| 449 | return -1; |
| 450 | } |
| 451 | |
| 452 | void* addr = shmat(shmid, NULL, 0); |
| 453 | if (addr == (void*) -1) { |
| 454 | wsLog("ERROR: could not attach to key=%d shmid=%d\n", key, shmid); |
| 455 | return -1; |
| 456 | } |
| 457 | |
| 458 | struct shmid_ds shmids; |
| 459 | int cc; |
| 460 | |
| 461 | cc = shmctl(shmid, IPC_STAT, &shmids); |
| 462 | if (cc != 0) { |
| 463 | wsLog("ERROR: could not IPC_STAT shmid=%d\n", shmid); |
| 464 | return -1; |
| 465 | } |
| 466 | *pLength = shmids.shm_segsz; |
| 467 | |
| 468 | *pAddr = addr; |
| 469 | *pShmid = shmid; |
| 470 | return 0; |
| 471 | } |
| 472 | |
| 473 | /* |
| 474 | * Attach to a SysV semaphore. |
| 475 | */ |
| 476 | static int attachToSem(int key, int* pSemid) |
| 477 | { |
| 478 | int semid; |
| 479 | |
| 480 | semid = semget(key, 0, 0); |
| 481 | if (semid == -1) { |
| 482 | wsLog("ERROR: failed to attach to semaphore key=%d\n", key); |
| 483 | return -1; |
| 484 | } |
| 485 | |
| 486 | *pSemid = semid; |
| 487 | return 0; |
| 488 | } |
| 489 | |
| 490 | /* |
| 491 | * "Adjust" a semaphore. |
| 492 | */ |
| 493 | static int adjustSem(int semid, int adj) |
| 494 | { |
| 495 | const int wait = 1; |
| 496 | struct sembuf op; |
| 497 | int cc; |
| 498 | |
| 499 | op.sem_num = 0; |
| 500 | op.sem_op = adj; |
| 501 | op.sem_flg = SEM_UNDO; |
| 502 | if (!wait) |
| 503 | op.sem_flg |= IPC_NOWAIT; |
| 504 | |
| 505 | cc = semop(semid, &op, 1); |
| 506 | if (cc != 0) { |
| 507 | if (wait || errno != EAGAIN) { |
| 508 | wsLog("Warning:" |
| 509 | " semaphore adjust by %d failed for semid=%d (errno=%d)\n", |
| 510 | adj, semid, errno); |
| 511 | } |
| 512 | return -1; |
| 513 | } |
| 514 | |
| 515 | return 0; |
| 516 | } |
| 517 | |
| 518 | /* |
| 519 | * Acquire the semaphore associated with a display. |
| 520 | */ |
| 521 | void wsLockDisplay(int displayIdx) |
| 522 | { |
| 523 | assert(displayIdx >= 0 && displayIdx < gWrapSim.numDisplays); |
| 524 | int semid = gWrapSim.display[displayIdx].semid; |
| 525 | |
| 526 | (void) adjustSem(semid, -1); |
| 527 | } |
| 528 | |
| 529 | /* |
| 530 | * Acquire the semaphore associated with a display. |
| 531 | */ |
| 532 | void wsUnlockDisplay(int displayIdx) |
| 533 | { |
| 534 | assert(displayIdx >= 0 && displayIdx < gWrapSim.numDisplays); |
| 535 | int semid = gWrapSim.display[displayIdx].semid; |
| 536 | |
| 537 | (void) adjustSem(semid, 1); |
| 538 | } |
| 539 | |
| 540 | /* |
| 541 | * Process the display config from the simulator |
| 542 | * |
| 543 | * Right now this is a blob of raw data that looks like this: |
| 544 | * +00 magic number |
| 545 | * +04 #of displays |
| 546 | * +08 display 0: |
| 547 | * +00 width |
| 548 | * +04 height |
| 549 | * +08 format |
| 550 | * +0c refresh rate |
| 551 | * +10 shmem key |
| 552 | * +1c display 1... |
| 553 | */ |
| 554 | static int handleDisplayConfig(const int* pData, int length) |
| 555 | { |
| 556 | int numDisplays; |
| 557 | |
| 558 | if (length < 8) { |
| 559 | wsLog("Bad display config: length is %d\n", length); |
| 560 | return -1; |
| 561 | } |
| 562 | assert(*pData == kDisplayConfigMagic); |
| 563 | |
| 564 | /* |
| 565 | * Pull out the #of displays. If it looks valid, configure the runtime. |
| 566 | */ |
| 567 | pData++; // skip over magic |
| 568 | numDisplays = *pData++; |
| 569 | |
| 570 | if (numDisplays <= 0 || numDisplays > kMaxDisplays) { |
| 571 | wsLog("Bizarre display count %d\n", numDisplays); |
| 572 | return -1; |
| 573 | } |
| 574 | if (length != 8 + numDisplays * kValuesPerDisplay * (int)sizeof(int)) { |
| 575 | wsLog("Bad display config: length is %d (expected %d)\n", |
| 576 | length, 8 + numDisplays * kValuesPerDisplay * (int)sizeof(int)); |
| 577 | return -1; |
| 578 | } |
| 579 | |
| 580 | /* |
| 581 | * Extract the config values. |
| 582 | * |
| 583 | * The runtime doesn't support multiple devices, so we don't either. |
| 584 | */ |
| 585 | int i; |
| 586 | for (i = 0; i < numDisplays; i++) { |
| 587 | gWrapSim.display[i].width = pData[0]; |
| 588 | gWrapSim.display[i].height = pData[1]; |
| 589 | gWrapSim.display[i].shmemKey = pData[4]; |
| 590 | /* format/refresh no longer needed */ |
| 591 | |
| 592 | void* addr; |
| 593 | int shmid, semid; |
| 594 | long length; |
| 595 | if (attachToShmem(gWrapSim.display[i].shmemKey, &shmid, &addr, |
| 596 | &length) != 0) |
| 597 | { |
| 598 | wsLog("Unable to connect to shared memory\n"); |
| 599 | return -1; |
| 600 | } |
| 601 | |
| 602 | if (attachToSem(gWrapSim.display[i].shmemKey, &semid) != 0) { |
| 603 | wsLog("Unable to attach to sempahore\n"); |
| 604 | return -1; |
| 605 | } |
| 606 | |
| 607 | gWrapSim.display[i].shmid = shmid; |
| 608 | gWrapSim.display[i].addr = addr; |
| 609 | gWrapSim.display[i].length = length; |
| 610 | gWrapSim.display[i].semid = semid; |
| 611 | |
| 612 | wsLog("Display %d: width=%d height=%d\n", |
| 613 | i, |
| 614 | gWrapSim.display[i].width, |
| 615 | gWrapSim.display[i].height); |
| 616 | wsLog(" shmem=0x%08x addr=%p len=%ld semid=%d\n", |
| 617 | gWrapSim.display[i].shmemKey, |
| 618 | gWrapSim.display[i].addr, |
| 619 | gWrapSim.display[i].length, |
| 620 | gWrapSim.display[i].semid); |
| 621 | |
| 622 | pData += kValuesPerDisplay; |
| 623 | } |
| 624 | gWrapSim.numDisplays = numDisplays; |
| 625 | |
| 626 | return 0; |
| 627 | } |
| 628 | |
| 629 | |
| 630 | /* |
| 631 | * Initialize our connection to the simulator, which will be listening on |
| 632 | * a UNIX domain socket. |
| 633 | * |
| 634 | * On success, this configures gWrapSim.simulatorFd and returns 0. |
| 635 | */ |
| 636 | static int openSimConnection(const char* name) |
| 637 | { |
| 638 | int result = -1; |
| 639 | char* fileName = NULL; |
| 640 | int sock = -1; |
| 641 | int cc; |
| 642 | |
| 643 | assert(gWrapSim.simulatorFd == -1); |
| 644 | |
| 645 | fileName = makeFilename(name); |
| 646 | |
| 647 | struct sockaddr_un addr; |
| 648 | |
| 649 | sock = socket(AF_UNIX, SOCK_STREAM, 0); |
| 650 | if (sock < 0) { |
| 651 | wsLog("UNIX domain socket create failed (errno=%d)\n", errno); |
| 652 | goto bail; |
| 653 | } |
| 654 | |
| 655 | /* connect to socket; fails if file doesn't exist */ |
| 656 | strcpy(addr.sun_path, fileName); // max 108 bytes |
| 657 | addr.sun_family = AF_UNIX; |
| 658 | cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr)); |
| 659 | if (cc < 0) { |
| 660 | // ENOENT means socket file doesn't exist |
| 661 | // ECONNREFUSED means socket exists but nobody is listening |
| 662 | wsLog("AF_UNIX connect failed for '%s': %s\n", |
| 663 | fileName, strerror(errno)); |
| 664 | goto bail; |
| 665 | } |
| 666 | |
| 667 | gWrapSim.simulatorFd = sock; |
| 668 | sock = -1; |
| 669 | |
| 670 | result = 0; |
| 671 | wsLog("+++ connected to '%s'\n", fileName); |
| 672 | |
| 673 | bail: |
| 674 | if (sock >= 0) |
| 675 | _ws_close(sock); |
| 676 | free(fileName); |
| 677 | return result; |
| 678 | } |
| 679 | |
| 680 | /* |
| 681 | * Prepare communication with the front end. We wait for a "hello" from |
| 682 | * the other side, and respond in kind. |
| 683 | */ |
| 684 | static int prepSimConnection(void) |
| 685 | { |
| 686 | /* NOTE: this is endian-specific; we're x86 Linux only, so no problem */ |
| 687 | static const unsigned int hello = kHelloMsg; |
| 688 | static const unsigned int helloAck = kHelloAckMsg; |
| 689 | Message msg; |
| 690 | |
| 691 | if (Message_read(&msg, gWrapSim.simulatorFd) != 0) { |
| 692 | wsLog("hello read failed\n"); |
| 693 | return -1; |
| 694 | } |
| 695 | |
| 696 | if (memcmp(msg.mData, &hello, 4) != 0) { |
| 697 | wsLog("Got bad hello from peer\n"); |
| 698 | return -1; |
| 699 | } |
| 700 | |
| 701 | Message_release(&msg); |
| 702 | |
| 703 | msg.mType = kTypeRaw; |
| 704 | msg.mData = (unsigned char*) &helloAck; |
| 705 | msg.mLength = 4; |
| 706 | |
| 707 | if (Message_write(&msg, gWrapSim.simulatorFd) != 0) { |
| 708 | wsLog("hello ack write failed\n"); |
| 709 | return -1; |
| 710 | } |
| 711 | |
| 712 | return 0; |
| 713 | } |
| 714 | |
| 715 | /* |
| 716 | * Get the sim front-end configuration. We loop here until the sim claims |
| 717 | * to be done with us. |
| 718 | */ |
| 719 | static int getSimConfig(void) |
| 720 | { |
| 721 | Message msg; |
| 722 | int joinNewGroup, grabTerminal, done; |
| 723 | int result = -1; |
| 724 | |
| 725 | joinNewGroup = grabTerminal = done = 0; |
| 726 | Message_clear(&msg); // clear out msg->mData |
| 727 | |
| 728 | wsLog("+++ awaiting hardware configuration\n"); |
| 729 | while (!done) { |
| 730 | if (Message_read(&msg, gWrapSim.simulatorFd) != 0) { |
| 731 | wsLog("failed receiving config from parent\n"); |
| 732 | goto bail; |
| 733 | } |
| 734 | |
| 735 | if (msg.mType == kTypeCommand) { |
| 736 | int cmd, arg; |
| 737 | |
| 738 | if (getCommand(&msg, &cmd, &arg) != 0) |
| 739 | goto bail; |
| 740 | |
| 741 | switch (cmd) { |
| 742 | case kCommandGoAway: |
| 743 | wsLog("Simulator front-end is busy\n"); |
| 744 | goto bail; |
| 745 | case kCommandNewPGroup: |
| 746 | joinNewGroup = 1; |
| 747 | grabTerminal = (arg != 0); |
| 748 | wsLog("Simulator wants us to be in a new pgrp (term=%d)\n", |
| 749 | grabTerminal); |
| 750 | break; |
| 751 | case kCommandConfigDone: |
| 752 | done = 1; |
| 753 | break; |
| 754 | default: |
| 755 | wsLog("Got unexpected command %d/%d\n", cmd, arg); |
| 756 | break; |
| 757 | } |
| 758 | } else if (msg.mType == kTypeRaw) { |
| 759 | /* assumes 32-bit alignment and identical byte ordering */ |
| 760 | int* pData = (int*) msg.mData; |
| 761 | if (msg.mLength >= 4 && *pData == kDisplayConfigMagic) { |
| 762 | if (handleDisplayConfig(pData, msg.mLength) != 0) |
| 763 | goto bail; |
| 764 | } |
| 765 | } else if (msg.mType == kTypeConfig) { |
Andy McFadden | 26dc361 | 2010-05-21 10:58:40 -0700 | [diff] [blame] | 766 | const char* name = NULL; |
| 767 | const char* val = NULL; |
The Android Open Source Project | 52d4c30 | 2009-03-03 19:29:09 -0800 | [diff] [blame] | 768 | getConfig(&msg, &name, &val); |
| 769 | if(strcmp(name, "keycharmap") == 0) { |
| 770 | free((void*)gWrapSim.keyMap); |
| 771 | gWrapSim.keyMap = strdup(val); |
| 772 | } |
| 773 | } else { |
| 774 | wsLog("Unexpected msg type %d during startup\n", msg.mType); |
| 775 | goto bail; |
| 776 | } |
| 777 | |
| 778 | /* clear out the data field if necessary */ |
| 779 | Message_release(&msg); |
| 780 | } |
| 781 | |
| 782 | wsLog("Configuration received from simulator\n"); |
| 783 | |
| 784 | if (joinNewGroup) { |
| 785 | /* set pgid to pid */ |
| 786 | pid_t pgid = getpid(); |
| 787 | setpgid(0, pgid); |
| 788 | |
| 789 | /* |
| 790 | * Put our pgrp in the foreground. |
| 791 | * tcsetpgrp() from background process causes us to get a SIGTTOU, |
| 792 | * which is mostly harmless but makes tcsetpgrp() fail with EINTR. |
| 793 | */ |
| 794 | signal(SIGTTOU, SIG_IGN); |
| 795 | if (grabTerminal) { |
| 796 | if (tcsetpgrp(fileno(stdin), getpgrp()) != 0) { |
| 797 | wsLog("tcsetpgrp(%d, %d) failed (errno=%d)\n", |
| 798 | fileno(stdin), getpgrp(), errno); |
| 799 | } |
| 800 | wsLog("Set pgrp %d as foreground\n", (int) getpgrp()); |
| 801 | } |
| 802 | |
| 803 | /* tell the sim where we're at */ |
| 804 | Message msg; |
| 805 | setCommand(&msg, kCommandNewPGroupCreated, pgid); |
| 806 | Message_write(&msg, gWrapSim.simulatorFd); |
| 807 | Message_release(&msg); |
| 808 | } |
| 809 | |
| 810 | result = 0; |
| 811 | |
| 812 | bail: |
| 813 | /* make sure the data was freed */ |
| 814 | Message_release(&msg); |
| 815 | //wsLog("bailing, result=%d\n", result); |
| 816 | return result; |
| 817 | } |
| 818 | |
| 819 | /* |
| 820 | * Connect to the simulator and exchange pleasantries. |
| 821 | * |
| 822 | * Returns 0 on success. |
| 823 | */ |
| 824 | static int connectToSim(void) |
| 825 | { |
| 826 | if (openSimConnection(kAndroidPipeName) != 0) |
| 827 | return -1; |
| 828 | |
| 829 | if (prepSimConnection() != 0) |
| 830 | return -1; |
| 831 | |
| 832 | if (getSimConfig() != 0) |
| 833 | return -1; |
| 834 | |
| 835 | wsLog("+++ sim is ready to go\n"); |
| 836 | |
| 837 | return 0; |
| 838 | } |
| 839 | |
| 840 | /* |
| 841 | * Listen to the sim forever or until the front end shuts down, whichever |
| 842 | * comes first. |
| 843 | * |
| 844 | * All we're really getting here are key events. |
| 845 | */ |
| 846 | static void listenToSim(void) |
| 847 | { |
| 848 | wsLog("--- listening for input events from front end\n"); |
| 849 | |
| 850 | while (1) { |
| 851 | Message msg; |
| 852 | |
| 853 | Message_clear(&msg); |
| 854 | if (Message_read(&msg, gWrapSim.simulatorFd) != 0) { |
| 855 | wsLog("--- sim message read failed\n"); |
| 856 | return; |
| 857 | } |
| 858 | |
| 859 | if (msg.mType == kTypeCommand) { |
| 860 | int cmd, arg; |
| 861 | |
| 862 | if (getCommand(&msg, &cmd, &arg) != 0) { |
| 863 | wsLog("bad command from sim?\n"); |
| 864 | continue; |
| 865 | } |
| 866 | |
| 867 | switch (cmd) { |
| 868 | case kCommandQuit: |
| 869 | wsLog("--- sim sent us a QUIT message\n"); |
| 870 | return; |
| 871 | case kCommandKeyDown: |
| 872 | wsLog("KEY DOWN: %d\n", arg); |
| 873 | wsSendSimKeyEvent(arg, 1); |
| 874 | break; |
| 875 | case kCommandKeyUp: |
| 876 | wsLog("KEY UP: %d\n", arg); |
| 877 | wsSendSimKeyEvent(arg, 0); |
| 878 | break; |
| 879 | default: |
| 880 | wsLog("--- sim sent unrecognized command %d\n", cmd); |
| 881 | break; |
| 882 | } |
| 883 | |
| 884 | Message_release(&msg); |
| 885 | } else if (msg.mType == kTypeCommandExt) { |
| 886 | int cmd, arg0, arg1, arg2; |
| 887 | |
| 888 | if (getCommandExt(&msg, &cmd, &arg0, &arg1, &arg2) != 0) { |
| 889 | wsLog("bad ext-command from sim?\n"); |
| 890 | continue; |
| 891 | } |
| 892 | |
| 893 | switch (cmd) { |
| 894 | case kCommandTouch: |
| 895 | wsSendSimTouchEvent(arg0, arg1, arg2); |
| 896 | break; |
| 897 | } |
| 898 | |
| 899 | Message_release(&msg); |
| 900 | } else { |
| 901 | wsLog("--- sim sent non-command message, type=%d\n", msg.mType); |
| 902 | } |
| 903 | } |
| 904 | |
| 905 | assert(0); // not reached |
| 906 | } |
| 907 | |
| 908 | |
| 909 | /* |
| 910 | * Tell the simulator front-end that the display has been updated. |
| 911 | */ |
| 912 | void wsPostDisplayUpdate(int displayIdx) |
| 913 | { |
| 914 | if (gWrapSim.simulatorFd < 0) { |
| 915 | wsLog("Not posting display update -- sim not ready\n"); |
| 916 | return; |
| 917 | } |
| 918 | |
| 919 | Message msg; |
| 920 | |
| 921 | setCommand(&msg, kCommandUpdateDisplay, displayIdx); |
| 922 | Message_write(&msg, gWrapSim.simulatorFd); |
| 923 | Message_release(&msg); |
| 924 | } |
| 925 | |
| 926 | /* |
| 927 | * Send a log message to the front-end. |
| 928 | */ |
| 929 | void wsPostLogMessage(int logPrio, const char* tag, const char* message) |
| 930 | { |
| 931 | if (gWrapSim.simulatorFd < 0) { |
| 932 | wsLog("Not posting log message -- sim not ready\n"); |
| 933 | return; |
| 934 | } |
| 935 | |
| 936 | time_t when = time(NULL); |
| 937 | int pid = (int) getpid(); |
| 938 | int tagLen, messageLen, totalLen; |
| 939 | |
| 940 | tagLen = strlen(tag) +1; |
| 941 | messageLen = strlen(message) +1; |
| 942 | totalLen = sizeof(int) * 3 + tagLen + messageLen; |
| 943 | unsigned char outBuf[totalLen]; |
| 944 | unsigned char* cp = outBuf; |
| 945 | |
| 946 | /* See Message::set/getLogBundle() in simulator/MessageStream.cpp. */ |
| 947 | memcpy(cp, &when, sizeof(int)); |
| 948 | cp += sizeof(int); |
| 949 | memcpy(cp, &logPrio, sizeof(int)); |
| 950 | cp += sizeof(int); |
| 951 | memcpy(cp, &pid, sizeof(int)); |
| 952 | cp += sizeof(int); |
| 953 | memcpy(cp, tag, tagLen); |
| 954 | cp += tagLen; |
| 955 | memcpy(cp, message, messageLen); |
| 956 | cp += messageLen; |
| 957 | |
| 958 | assert(cp - outBuf == totalLen); |
| 959 | |
| 960 | Message msg; |
| 961 | msg.mType = kTypeLogBundle; |
| 962 | msg.mData = outBuf; |
| 963 | msg.mLength = totalLen; |
| 964 | Message_write(&msg, gWrapSim.simulatorFd); |
| 965 | |
| 966 | msg.mData = NULL; // don't free |
| 967 | Message_release(&msg); |
| 968 | } |
| 969 | |
| 970 | /* |
| 971 | * Turn the vibrating notification device on or off. |
| 972 | */ |
| 973 | void wsEnableVibration(int vibrateOn) |
| 974 | { |
| 975 | if (gWrapSim.simulatorFd < 0) { |
| 976 | wsLog("Not posting vibrator update -- sim not ready\n"); |
| 977 | return; |
| 978 | } |
| 979 | |
| 980 | Message msg; |
| 981 | |
| 982 | //wsLog("+++ sending vibrate:%d\n", vibrateOn); |
| 983 | |
| 984 | setCommand(&msg, kCommandVibrate, vibrateOn); |
| 985 | Message_write(&msg, gWrapSim.simulatorFd); |
| 986 | Message_release(&msg); |
| 987 | } |
| 988 | |