summaryrefslogtreecommitdiffstats
path: root/vm/jdwp/JdwpAdb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vm/jdwp/JdwpAdb.cpp')
-rw-r--r--vm/jdwp/JdwpAdb.cpp746
1 files changed, 0 insertions, 746 deletions
diff --git a/vm/jdwp/JdwpAdb.cpp b/vm/jdwp/JdwpAdb.cpp
deleted file mode 100644
index 8fb539134..000000000
--- a/vm/jdwp/JdwpAdb.cpp
+++ /dev/null
@@ -1,746 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "jdwp/JdwpPriv.h"
-#include "jdwp/JdwpHandler.h"
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <errno.h>
-#include <unistd.h>
-#include <cutils/sockets.h>
-
-/*
- * The JDWP <-> ADB transport protocol is explained in detail
- * in system/core/adb/jdwp_service.c. Here's a summary.
- *
- * 1/ when the JDWP thread starts, it tries to connect to a Unix
- * domain stream socket (@jdwp-control) that is opened by the
- * ADB daemon.
- *
- * 2/ it then sends the current process PID as a string of 4 hexadecimal
- * chars (no terminating zero)
- *
- * 3/ then, it uses recvmsg to receive file descriptors from the
- * daemon. each incoming file descriptor is a pass-through to
- * a given JDWP debugger, that can be used to read the usual
- * JDWP-handshake, etc...
- */
-
-#define kInputBufferSize 8192
-
-#define kMagicHandshake "JDWP-Handshake"
-#define kMagicHandshakeLen (sizeof(kMagicHandshake)-1)
-
-#define kJdwpControlName "\0jdwp-control"
-#define kJdwpControlNameLen (sizeof(kJdwpControlName)-1)
-
-struct JdwpNetState : public JdwpNetStateBase {
- int controlSock;
- bool awaitingHandshake;
- bool shuttingDown;
- int wakeFds[2];
-
- int inputCount;
- unsigned char inputBuffer[kInputBufferSize];
-
- socklen_t controlAddrLen;
- union {
- struct sockaddr_un controlAddrUn;
- struct sockaddr controlAddrPlain;
- } controlAddr;
-
- JdwpNetState()
- {
- controlSock = -1;
- awaitingHandshake = false;
- shuttingDown = false;
- wakeFds[0] = -1;
- wakeFds[1] = -1;
-
- inputCount = 0;
-
- controlAddr.controlAddrUn.sun_family = AF_UNIX;
- controlAddrLen = sizeof(controlAddr.controlAddrUn.sun_family) +
- kJdwpControlNameLen;
- memcpy(controlAddr.controlAddrUn.sun_path, kJdwpControlName,
- kJdwpControlNameLen);
- }
-};
-
-static void
-adbStateFree( JdwpNetState* netState )
-{
- if (netState == NULL)
- return;
-
- if (netState->clientSock >= 0) {
- shutdown(netState->clientSock, SHUT_RDWR);
- close(netState->clientSock);
- }
- if (netState->controlSock >= 0) {
- shutdown(netState->controlSock, SHUT_RDWR);
- close(netState->controlSock);
- }
- if (netState->wakeFds[0] >= 0) {
- close(netState->wakeFds[0]);
- netState->wakeFds[0] = -1;
- }
- if (netState->wakeFds[1] >= 0) {
- close(netState->wakeFds[1]);
- netState->wakeFds[1] = -1;
- }
-
- delete netState;
-}
-
-/*
- * Do initial prep work, e.g. binding to ports and opening files. This
- * runs in the main thread, before the JDWP thread starts, so it shouldn't
- * do anything that might block forever.
- */
-static bool startup(struct JdwpState* state, const JdwpStartupParams* pParams)
-{
- JdwpNetState* netState;
-
- ALOGV("ADB transport startup");
-
- state->netState = netState = new JdwpNetState;
- if (netState == NULL)
- return false;
-
- return true;
-}
-
-/*
- * Receive a file descriptor from ADB. The fd can be used to communicate
- * directly with a debugger or DDMS.
- *
- * Returns the file descriptor on success. On failure, returns -1 and
- * closes netState->controlSock.
- */
-static int receiveClientFd(JdwpNetState* netState)
-{
- struct msghdr msg;
- struct cmsghdr* cmsg;
- struct iovec iov;
- char dummy = '!';
- union {
- struct cmsghdr cm;
- char buffer[CMSG_SPACE(sizeof(int))];
- } cm_un;
- int ret;
-
- iov.iov_base = &dummy;
- iov.iov_len = 1;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_flags = 0;
- msg.msg_control = cm_un.buffer;
- msg.msg_controllen = sizeof(cm_un.buffer);
-
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = msg.msg_controllen;
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- ((int*)(void*)CMSG_DATA(cmsg))[0] = -1;
-
- do {
- ret = recvmsg(netState->controlSock, &msg, 0);
- } while (ret < 0 && errno == EINTR);
-
- if (ret <= 0) {
- if (ret < 0) {
- ALOGW("receiving file descriptor from ADB failed (socket %d): %s",
- netState->controlSock, strerror(errno));
- }
- close(netState->controlSock);
- netState->controlSock = -1;
- return -1;
- }
-
- return ((int*)(void*)CMSG_DATA(cmsg))[0];
-}
-
-/*
- * Block forever, waiting for a debugger to connect to us. Called from the
- * JDWP thread.
- *
- * This needs to un-block and return "false" if the VM is shutting down. It
- * should return "true" when it successfully accepts a connection.
- */
-static bool acceptConnection(struct JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
- int retryCount = 0;
-
- /* first, ensure that we get a connection to the ADB daemon */
-
-retry:
- if (netState->shuttingDown)
- return false;
-
- if (netState->controlSock < 0) {
- int sleep_ms = 500;
- const int sleep_max_ms = 2*1000;
- char buff[5];
-
- netState->controlSock = socket(PF_UNIX, SOCK_STREAM, 0);
- if (netState->controlSock < 0) {
- ALOGE("Could not create ADB control socket:%s",
- strerror(errno));
- return false;
- }
-
- if (pipe(netState->wakeFds) < 0) {
- ALOGE("pipe failed");
- return false;
- }
-
- snprintf(buff, sizeof(buff), "%04x", getpid());
- buff[4] = 0;
-
- for (;;) {
- /*
- * If adbd isn't running, because USB debugging was disabled or
- * perhaps the system is restarting it for "adb root", the
- * connect() will fail. We loop here forever waiting for it
- * to come back.
- *
- * Waking up and polling every couple of seconds is generally a
- * bad thing to do, but we only do this if the application is
- * debuggable *and* adbd isn't running. Still, for the sake
- * of battery life, we should consider timing out and giving
- * up after a few minutes in case somebody ships an app with
- * the debuggable flag set.
- */
- int ret = connect(netState->controlSock,
- &netState->controlAddr.controlAddrPlain,
- netState->controlAddrLen);
- if (!ret) {
- if (!socket_peer_is_trusted(netState->controlSock)) {
- if (shutdown(netState->controlSock, SHUT_RDWR)) {
- ALOGE("trouble shutting down socket: %s", strerror(errno));
- }
- return false;
- }
-
- /* now try to send our pid to the ADB daemon */
- do {
- ret = send( netState->controlSock, buff, 4, 0 );
- } while (ret < 0 && errno == EINTR);
-
- if (ret >= 0) {
- ALOGV("PID sent as '%.*s' to ADB", 4, buff);
- break;
- }
-
- ALOGE("Weird, can't send JDWP process pid to ADB: %s",
- strerror(errno));
- return false;
- }
- ALOGV("Can't connect to ADB control socket:%s",
- strerror(errno));
-
- usleep( sleep_ms*1000 );
-
- sleep_ms += (sleep_ms >> 1);
- if (sleep_ms > sleep_max_ms)
- sleep_ms = sleep_max_ms;
-
- if (netState->shuttingDown)
- return false;
- }
- }
-
- ALOGV("trying to receive file descriptor from ADB");
- /* now we can receive a client file descriptor */
- netState->clientSock = receiveClientFd(netState);
- if (netState->shuttingDown)
- return false; // suppress logs and additional activity
-
- if (netState->clientSock < 0) {
- if (++retryCount > 5) {
- ALOGE("adb connection max retries exceeded");
- return false;
- }
- goto retry;
- } else {
- ALOGV("received file descriptor %d from ADB", netState->clientSock);
- netState->awaitingHandshake = 1;
- netState->inputCount = 0;
- return true;
- }
-}
-
-/*
- * Connect out to a debugger (for server=n). Not required.
- */
-static bool establishConnection(struct JdwpState* state)
-{
- return false;
-}
-
-/*
- * Close a connection from a debugger (which may have already dropped us).
- * Only called from the JDWP thread.
- */
-static void closeConnection(struct JdwpState* state)
-{
- JdwpNetState* netState;
-
- assert(state != NULL && state->netState != NULL);
-
- netState = state->netState;
- if (netState->clientSock < 0)
- return;
-
- ALOGV("+++ closed JDWP <-> ADB connection");
-
- close(netState->clientSock);
- netState->clientSock = -1;
-}
-
-/*
- * Close all network stuff, including the socket we use to listen for
- * new connections.
- *
- * May be called from a non-JDWP thread, e.g. when the VM is shutting down.
- */
-static void adbStateShutdown(struct JdwpNetState* netState)
-{
- int controlSock;
- int clientSock;
-
- if (netState == NULL)
- return;
-
- netState->shuttingDown = true;
-
- clientSock = netState->clientSock;
- if (clientSock >= 0) {
- shutdown(clientSock, SHUT_RDWR);
- netState->clientSock = -1;
- }
-
- controlSock = netState->controlSock;
- if (controlSock >= 0) {
- shutdown(controlSock, SHUT_RDWR);
- netState->controlSock = -1;
- }
-
- if (netState->wakeFds[1] >= 0) {
- ALOGV("+++ writing to wakePipe");
- TEMP_FAILURE_RETRY(write(netState->wakeFds[1], "", 1));
- }
-}
-
-static void netShutdown(JdwpState* state)
-{
- adbStateShutdown(state->netState);
-}
-
-/*
- * Free up anything we put in state->netState. This is called after
- * "netShutdown", after the JDWP thread has stopped.
- */
-static void netFree(struct JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
-
- adbStateFree(netState);
-}
-
-/*
- * Is a debugger connected to us?
- */
-static bool isConnected(struct JdwpState* state)
-{
- return (state->netState != NULL &&
- state->netState->clientSock >= 0);
-}
-
-/*
- * Are we still waiting for the JDWP handshake?
- */
-static bool awaitingHandshake(struct JdwpState* state)
-{
- return state->netState->awaitingHandshake;
-}
-
-/*
- * Figure out if we have a full packet in the buffer.
- */
-static bool haveFullPacket(JdwpNetState* netState)
-{
- long length;
-
- if (netState->awaitingHandshake)
- return (netState->inputCount >= (int) kMagicHandshakeLen);
-
- if (netState->inputCount < 4)
- return false;
-
- length = get4BE(netState->inputBuffer);
- return (netState->inputCount >= length);
-}
-
-/*
- * Consume bytes from the buffer.
- *
- * This would be more efficient with a circular buffer. However, we're
- * usually only going to find one packet, which is trivial to handle.
- */
-static void consumeBytes(JdwpNetState* netState, int count)
-{
- assert(count > 0);
- assert(count <= netState->inputCount);
-
- if (count == netState->inputCount) {
- netState->inputCount = 0;
- return;
- }
-
- memmove(netState->inputBuffer, netState->inputBuffer + count,
- netState->inputCount - count);
- netState->inputCount -= count;
-}
-
-/*
- * Handle a packet. Returns "false" if we encounter a connection-fatal error.
- */
-static bool handlePacket(JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
- const unsigned char* buf = netState->inputBuffer;
- JdwpReqHeader hdr;
- u4 length, id;
- u1 flags, cmdSet, cmd;
- u2 error;
- bool reply;
- int dataLen;
-
- cmd = cmdSet = 0; // shut up gcc
-
- length = read4BE(&buf);
- id = read4BE(&buf);
- flags = read1(&buf);
- if ((flags & kJDWPFlagReply) != 0) {
- reply = true;
- error = read2BE(&buf);
- } else {
- reply = false;
- cmdSet = read1(&buf);
- cmd = read1(&buf);
- }
-
- assert((int) length <= netState->inputCount);
- dataLen = length - (buf - netState->inputBuffer);
-
- if (!reply) {
- ExpandBuf* pReply = expandBufAlloc();
-
- hdr.length = length;
- hdr.id = id;
- hdr.cmdSet = cmdSet;
- hdr.cmd = cmd;
- dvmJdwpProcessRequest(state, &hdr, buf, dataLen, pReply);
- if (expandBufGetLength(pReply) > 0) {
- ssize_t cc = netState->writePacket(pReply);
-
- if (cc != (ssize_t) expandBufGetLength(pReply)) {
- ALOGE("Failed sending reply to debugger: %s", strerror(errno));
- expandBufFree(pReply);
- return false;
- }
- } else {
- ALOGW("No reply created for set=%d cmd=%d", cmdSet, cmd);
- }
- expandBufFree(pReply);
- } else {
- ALOGV("reply?!");
- assert(false);
- }
-
- ALOGV("----------");
-
- consumeBytes(netState, length);
- return true;
-}
-
-/*
- * Process incoming data. If no data is available, this will block until
- * some arrives.
- *
- * If we get a full packet, handle it.
- *
- * To take some of the mystery out of life, we want to reject incoming
- * connections if we already have a debugger attached. If we don't, the
- * debugger will just mysteriously hang until it times out. We could just
- * close the listen socket, but there's a good chance we won't be able to
- * bind to the same port again, which would confuse utilities.
- *
- * Returns "false" on error (indicating that the connection has been severed),
- * "true" if things are still okay.
- */
-static bool processIncoming(JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
- int readCount;
-
- assert(netState->clientSock >= 0);
-
- if (!haveFullPacket(netState)) {
- /* read some more, looping until we have data */
- errno = 0;
- while (1) {
- int selCount;
- fd_set readfds;
- int maxfd = -1;
- int fd;
-
- FD_ZERO(&readfds);
-
- /* configure fds; note these may get zapped by another thread */
- fd = netState->controlSock;
- if (fd >= 0) {
- FD_SET(fd, &readfds);
- if (maxfd < fd)
- maxfd = fd;
- }
- fd = netState->clientSock;
- if (fd >= 0) {
- FD_SET(fd, &readfds);
- if (maxfd < fd)
- maxfd = fd;
- }
- fd = netState->wakeFds[0];
- if (fd >= 0) {
- FD_SET(fd, &readfds);
- if (maxfd < fd)
- maxfd = fd;
- } else {
- ALOGI("NOTE: entering select w/o wakepipe");
- }
-
- if (maxfd < 0) {
- ALOGV("+++ all fds are closed");
- return false;
- }
-
- /*
- * Select blocks until it sees activity on the file descriptors.
- * Closing the local file descriptor does not count as activity,
- * so we can't rely on that to wake us up (it works for read()
- * and accept(), but not select()).
- *
- * We can do one of three things: (1) send a signal and catch
- * EINTR, (2) open an additional fd ("wakePipe") and write to
- * it when it's time to exit, or (3) time out periodically and
- * re-issue the select. We're currently using #2, as it's more
- * reliable than #1 and generally better than #3. Wastes two fds.
- */
- selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
- if (selCount < 0) {
- if (errno == EINTR)
- continue;
- ALOGE("select failed: %s", strerror(errno));
- goto fail;
- }
-
- if (netState->wakeFds[0] >= 0 &&
- FD_ISSET(netState->wakeFds[0], &readfds))
- {
- ALOGD("Got wake-up signal, bailing out of select");
- goto fail;
- }
- if (netState->controlSock >= 0 &&
- FD_ISSET(netState->controlSock, &readfds))
- {
- int sock = receiveClientFd(netState);
- if (sock >= 0) {
- ALOGI("Ignoring second debugger -- accepting and dropping");
- close(sock);
- } else {
- assert(netState->controlSock < 0);
- /*
- * Remote side most likely went away, so our next read
- * on netState->clientSock will fail and throw us out
- * of the loop.
- */
- }
- }
- if (netState->clientSock >= 0 &&
- FD_ISSET(netState->clientSock, &readfds))
- {
- readCount = read(netState->clientSock,
- netState->inputBuffer + netState->inputCount,
- sizeof(netState->inputBuffer) - netState->inputCount);
- if (readCount < 0) {
- /* read failed */
- if (errno != EINTR)
- goto fail;
- ALOGD("+++ EINTR hit");
- return true;
- } else if (readCount == 0) {
- /* EOF hit -- far end went away */
- ALOGV("+++ peer disconnected");
- goto fail;
- } else
- break;
- }
- }
-
- netState->inputCount += readCount;
- if (!haveFullPacket(netState))
- return true; /* still not there yet */
- }
-
- /*
- * Special-case the initial handshake. For some bizarre reason we're
- * expected to emulate bad tty settings by echoing the request back
- * exactly as it was sent. Note the handshake is always initiated by
- * the debugger, no matter who connects to whom.
- *
- * Other than this one case, the protocol [claims to be] stateless.
- */
- if (netState->awaitingHandshake) {
- int cc;
-
- if (memcmp(netState->inputBuffer,
- kMagicHandshake, kMagicHandshakeLen) != 0)
- {
- ALOGE("ERROR: bad handshake '%.14s'", netState->inputBuffer);
- goto fail;
- }
-
- errno = 0;
- cc = TEMP_FAILURE_RETRY(write(netState->clientSock, netState->inputBuffer,
- kMagicHandshakeLen));
- if (cc != kMagicHandshakeLen) {
- ALOGE("Failed writing handshake bytes: %s (%d of %d)",
- strerror(errno), cc, (int) kMagicHandshakeLen);
- goto fail;
- }
-
- consumeBytes(netState, kMagicHandshakeLen);
- netState->awaitingHandshake = false;
- ALOGV("+++ handshake complete");
- return true;
- }
-
- /*
- * Handle this packet.
- */
- return handlePacket(state);
-
-fail:
- closeConnection(state);
- return false;
-}
-
-/*
- * Send a request.
- *
- * The entire packet must be sent with a single write() call to avoid
- * threading issues.
- *
- * Returns "true" if it was sent successfully.
- */
-static bool sendRequest(JdwpState* state, ExpandBuf* pReq)
-{
- JdwpNetState* netState = state->netState;
-
- if (netState->clientSock < 0) {
- /* can happen with some DDMS events */
- ALOGV("NOT sending request -- no debugger is attached");
- return false;
- }
-
- errno = 0;
-
- ssize_t cc = netState->writePacket(pReq);
-
- if (cc != (ssize_t) expandBufGetLength(pReq)) {
- ALOGE("Failed sending req to debugger: %s (%d of %d)",
- strerror(errno), (int) cc, (int) expandBufGetLength(pReq));
- return false;
- }
-
- return true;
-}
-
-/*
- * Send a request that was split into multiple buffers.
- *
- * The entire packet must be sent with a single writev() call to avoid
- * threading issues.
- *
- * Returns "true" if it was sent successfully.
- */
-static bool sendBufferedRequest(JdwpState* state, const struct iovec* iov,
- int iovcnt)
-{
- JdwpNetState* netState = state->netState;
-
- if (netState->clientSock < 0) {
- /* can happen with some DDMS events */
- ALOGV("NOT sending request -- no debugger is attached");
- return false;
- }
-
- size_t expected = 0;
- int i;
- for (i = 0; i < iovcnt; i++)
- expected += iov[i].iov_len;
-
- ssize_t actual = netState->writeBufferedPacket(iov, iovcnt);
-
- if ((size_t)actual != expected) {
- ALOGE("Failed sending b-req to debugger: %s (%d of %zu)",
- strerror(errno), (int) actual, expected);
- return false;
- }
-
- return true;
-}
-
-
-/*
- * Our functions.
- */
-static const JdwpTransport socketTransport = {
- startup,
- acceptConnection,
- establishConnection,
- closeConnection,
- netShutdown,
- netFree,
- isConnected,
- awaitingHandshake,
- processIncoming,
- sendRequest,
- sendBufferedRequest
-};
-
-/*
- * Return our set.
- */
-const JdwpTransport* dvmJdwpAndroidAdbTransport()
-{
- return &socketTransport;
-}