Methods Summary |
---|
synchronized java.nio.channels.SocketChannel | accept()Accept a new connection, but only if we don't already have one.
Must be synchronized with other uses of mChannel and mPreBuffer.
Returns "null" if we're already talking to somebody.
return accept(mListenChannel);
|
synchronized java.nio.channels.SocketChannel | accept(java.nio.channels.ServerSocketChannel listenChan)Accept a new connection from the specified listen channel. This
is so we can listen on a dedicated port for the "current" client,
where "current" is constantly in flux.
Must be synchronized with other uses of mChannel and mPreBuffer.
Returns "null" if we're already talking to somebody.
if (listenChan != null) {
SocketChannel newChan;
newChan = listenChan.accept();
if (mChannel != null) {
Log.w("ddms", "debugger already talking to " + mClient
+ " on " + mListenPort);
newChan.close();
return null;
}
mChannel = newChan;
mChannel.configureBlocking(false); // required for Selector
mConnState = ST_AWAIT_SHAKE;
return mChannel;
}
return null;
|
synchronized void | close()Close the socket that's listening for new connections and (if
we're connected) the debugger data socket.
try {
if (mListenChannel != null) {
mListenChannel.close();
}
mListenChannel = null;
closeData();
} catch (IOException ioe) {
Log.w("ddms", "Failed to close listener " + this);
}
|
synchronized void | closeData()Close the data connection only.
try {
if (mChannel != null) {
mChannel.close();
mChannel = null;
mConnState = ST_NOT_CONNECTED;
ClientData cd = mClient.getClientData();
cd.setDebuggerConnectionStatus(ClientData.DEBUGGER_DEFAULT);
mClient.update(Client.CHANGE_DEBUGGER_INTEREST);
}
} catch (IOException ioe) {
Log.w("ddms", "Failed to close data " + this);
}
|
void | forwardPacketToClient(JdwpPacket packet)Forward a packet to the client.
"mClient" will never be null, though it's possible that the channel
in the client has closed and our send attempt will fail.
Consumes the packet.
mClient.sendAndConsume(packet);
|
Client | getClient()Return the Client being debugged.
return mClient;
|
JdwpPacket | getJdwpPacket()Return information for the first full JDWP packet in the buffer.
If we don't yet have a full packet, return null.
If we haven't yet received the JDWP handshake, we watch for it here
and consume it without admitting to have done so. We also send
the handshake response to the debugger, along with any pending
pre-connection data, which is why this can throw an IOException.
/*
* On entry, the data starts at offset 0 and ends at "position".
* "limit" is set to the buffer capacity.
*/
if (mConnState == ST_AWAIT_SHAKE) {
int result;
result = JdwpPacket.findHandshake(mReadBuffer);
//Log.v("ddms", "findHand: " + result);
switch (result) {
case JdwpPacket.HANDSHAKE_GOOD:
Log.i("ddms", "Good handshake from debugger");
JdwpPacket.consumeHandshake(mReadBuffer);
sendHandshake();
mConnState = ST_READY;
ClientData cd = mClient.getClientData();
cd.setDebuggerConnectionStatus(ClientData.DEBUGGER_ATTACHED);
mClient.update(Client.CHANGE_DEBUGGER_INTEREST);
// see if we have another packet in the buffer
return getJdwpPacket();
case JdwpPacket.HANDSHAKE_BAD:
// not a debugger, throw an exception so we drop the line
Log.i("ddms", "Bad handshake from debugger");
throw new IOException("bad handshake");
case JdwpPacket.HANDSHAKE_NOTYET:
break;
default:
Log.e("ddms", "Unknown packet while waiting for client handshake");
}
return null;
} else if (mConnState == ST_READY) {
if (mReadBuffer.position() != 0) {
Log.v("ddms", "Checking " + mReadBuffer.position() + " bytes");
}
return JdwpPacket.findPacket(mReadBuffer);
} else {
Log.e("ddms", "Receiving data in state = " + mConnState);
}
return null;
|
boolean | isDebuggerAttached()Returns "true" if a debugger is currently attached to us.
return mChannel != null;
|
void | read()Read data from our channel.
This is called when data is known to be available, and we don't yet
have a full packet in the buffer. If the buffer is at capacity,
expand it.
int count;
if (mReadBuffer.position() == mReadBuffer.capacity()) {
if (mReadBuffer.capacity() * 2 > MAX_BUF_SIZE) {
throw new BufferOverflowException();
}
Log.d("ddms", "Expanding read buffer to "
+ mReadBuffer.capacity() * 2);
ByteBuffer newBuffer =
ByteBuffer.allocate(mReadBuffer.capacity() * 2);
mReadBuffer.position(0);
newBuffer.put(mReadBuffer); // leaves "position" at end
mReadBuffer = newBuffer;
}
count = mChannel.read(mReadBuffer);
Log.v("ddms", "Read " + count + " bytes from " + this);
if (count < 0) throw new IOException("read failed");
|
void | registerListener(java.nio.channels.Selector sel)Register the debugger's listen socket with the Selector.
mListenChannel.register(sel, SelectionKey.OP_ACCEPT, this);
|
synchronized void | sendAndConsume(JdwpPacket packet)Send a packet to the debugger.
Ideally, we can do this with a single channel write. If that doesn't
happen, we have to prevent anybody else from writing to the channel
until this packet completes, so we synchronize on the channel.
Another goal is to avoid unnecessary buffer copies, so we write
directly out of the JdwpPacket's ByteBuffer.
We must synchronize on "mChannel" before writing to it. We want to
coordinate the buffered data with mChannel creation, so this whole
method is synchronized.
if (mChannel == null) {
/*
* Buffer this up so we can send it to the debugger when it
* finally does connect. This is essential because the VM_START
* message might be telling the debugger that the VM is
* suspended. The alternative approach would be for us to
* capture and interpret VM_START and send it later if we
* didn't choose to un-suspend the VM for our own purposes.
*/
Log.d("ddms", "Saving packet 0x"
+ Integer.toHexString(packet.getId()));
packet.movePacket(mPreDataBuffer);
} else {
packet.writeAndConsume(mChannel);
}
|
private synchronized void | sendHandshake()Send the handshake to the debugger. We also send along any packets
we already received from the client (usually just a VM_START event,
if anything at all).
ByteBuffer tempBuffer = ByteBuffer.allocate(JdwpPacket.HANDSHAKE_LEN);
JdwpPacket.putHandshake(tempBuffer);
int expectedLength = tempBuffer.position();
tempBuffer.flip();
if (mChannel.write(tempBuffer) != expectedLength) {
throw new IOException("partial handshake write");
}
expectedLength = mPreDataBuffer.position();
if (expectedLength > 0) {
Log.d("ddms", "Sending " + mPreDataBuffer.position()
+ " bytes of saved data");
mPreDataBuffer.flip();
if (mChannel.write(mPreDataBuffer) != expectedLength) {
throw new IOException("partial pre-data write");
}
mPreDataBuffer.clear();
}
|
public java.lang.String | toString()Represent the Debugger as a string.
// mChannel != null means we have connection, ST_READY means it's going
return "[Debugger " + mListenPort + "-->" + mClient.getClientData().getPid()
+ ((mConnState != ST_READY) ? " inactive]" : " active]");
|