FileDocCategorySizeDatePackage
JdwpPacket.javaAPI DocAndroid 1.5 API11421Wed May 06 22:41:08 BST 2009com.android.ddmlib

JdwpPacket

public final class JdwpPacket extends Object
A JDWP packet, sitting at the start of a ByteBuffer somewhere. This allows us to wrap a "pointer" to the data with the results of decoding the packet. None of the operations here are synchronized. If multiple threads will be accessing the same ByteBuffers, external sync will be required. Use the constructor to create an empty packet, or "findPacket()" to wrap a JdwpPacket around existing data.

Fields Summary
public static final int
JDWP_HEADER_LEN
public static final int
HANDSHAKE_GOOD
public static final int
HANDSHAKE_NOTYET
public static final int
HANDSHAKE_BAD
private static final int
DDMS_CMD_SET
private static final int
DDMS_CMD
private static final int
REPLY_PACKET
private static final byte[]
mHandshake
public static final int
HANDSHAKE_LEN
private ByteBuffer
mBuffer
private int
mLength
private int
mId
private int
mFlags
private int
mCmdSet
private int
mCmd
private int
mErrCode
private boolean
mIsNew
private static int
mSerialId
Constructors Summary
JdwpPacket(ByteBuffer buf)
Create a new, empty packet, in "buf".



                
      
        mBuffer = buf;
        mIsNew = true;
    
Methods Summary
voidconsume()
Consume the JDWP packet. On entry and exit, "position" is the #of bytes in the buffer.

        //Log.d("ddms", "consuming " + mLength + " bytes");
        //Log.d("ddms", "  posn=" + mBuffer.position()
        //    + ", limit=" + mBuffer.limit());

        /*
         * The "flip" call sets "limit" equal to the position (usually the
         * end of data) and "position" equal to zero.
         *
         * compact() copies everything from "position" and "limit" to the
         * start of the buffer, sets "position" to the end of data, and
         * sets "limit" to the capacity.
         *
         * On entry, "position" is set to the amount of data in the buffer
         * and "limit" is set to the capacity.  We want to call flip()
         * so that position..limit spans our data, advance "position" past
         * the current packet, then compact.
         */
        mBuffer.flip();         // limit<-posn, posn<-0
        mBuffer.position(mLength);
        mBuffer.compact();      // shift posn...limit, posn<-pending data
        mLength = 0;
        //Log.d("ddms", "  after compact, posn=" + mBuffer.position()
        //    + ", limit=" + mBuffer.limit());
    
static voidconsumeHandshake(java.nio.ByteBuffer buf)
Remove the handshake string from the buffer. On entry and exit, "position" is the #of bytes in the buffer.

        // in theory, nothing else can have arrived, so this is overkill
        buf.flip();         // limit<-posn, posn<-0
        buf.position(mHandshake.length);
        buf.compact();      // shift posn...limit, posn<-pending data
    
static intfindHandshake(java.nio.ByteBuffer buf)
Like findPacket(), but when we're expecting the JDWP handshake. Returns one of: HANDSHAKE_GOOD - found handshake, looks good HANDSHAKE_BAD - found enough data, but it's wrong HANDSHAKE_NOTYET - not enough data has been read yet

        int count = buf.position();
        int i;

        if (count < mHandshake.length)
            return HANDSHAKE_NOTYET;

        for (i = mHandshake.length -1; i >= 0; --i) {
            if (buf.get(i) != mHandshake[i])
                return HANDSHAKE_BAD;
        }

        return HANDSHAKE_GOOD;
    
static com.android.ddmlib.JdwpPacketfindPacket(java.nio.ByteBuffer buf)
Find the JDWP packet at the start of "buf". The start is known, but the length has to be parsed out. On entry, the packet data in "buf" must start at offset 0 and end at "position". "limit" should be set to the buffer capacity. This method does not alter "buf"s attributes. Returns a new JdwpPacket if a full one is found in the buffer. If not, returns null. Throws an exception if the data doesn't look like a valid JDWP packet.

        int count = buf.position();
        int length, id, flags, cmdSet, cmd;

        if (count < JDWP_HEADER_LEN)
            return null;

        ByteOrder oldOrder = buf.order();
        buf.order(ChunkHandler.CHUNK_ORDER);

        length = buf.getInt(0x00);
        id = buf.getInt(0x04);
        flags = buf.get(0x08) & 0xff;
        cmdSet = buf.get(0x09) & 0xff;
        cmd = buf.get(0x0a) & 0xff;

        buf.order(oldOrder);

        if (length < JDWP_HEADER_LEN)
            throw new BadPacketException();
        if (count < length)
            return null;

        JdwpPacket pkt = new JdwpPacket(buf);
        //pkt.mBuffer = buf;
        pkt.mLength = length;
        pkt.mId = id;
        pkt.mFlags = flags;

        if ((flags & REPLY_PACKET) == 0) {
            pkt.mCmdSet = cmdSet;
            pkt.mCmd = cmd;
            pkt.mErrCode = -1;
        } else {
            pkt.mCmdSet = -1;
            pkt.mCmd = -1;
            pkt.mErrCode = cmdSet | (cmd << 8);
        }

        return pkt;
    
voidfinishPacket(int payloadLength)
Finish a packet created with newPacket(). This always creates a command packet, with the next serial number in sequence. We have to take "payloadLength" as an argument because we can't see the position in the "slice" returned by getPayload(). We could fish it out of the chunk header, but it's legal for there to be more than one chunk in a JDWP packet. On exit, "position" points to the end of the data.

        assert mIsNew;

        ByteOrder oldOrder = mBuffer.order();
        mBuffer.order(ChunkHandler.CHUNK_ORDER);

        mLength = JDWP_HEADER_LEN + payloadLength;
        mId = getNextSerial();
        mFlags = 0;
        mCmdSet = DDMS_CMD_SET;
        mCmd = DDMS_CMD;

        mBuffer.putInt(0x00, mLength);
        mBuffer.putInt(0x04, mId);
        mBuffer.put(0x08, (byte) mFlags);
        mBuffer.put(0x09, (byte) mCmdSet);
        mBuffer.put(0x0a, (byte) mCmd);

        mBuffer.order(oldOrder);
        mBuffer.position(mLength);
    
intgetId()
Return the packet's ID. For a reply packet, this allows us to match the reply with the original request.

        return mId;
    
intgetLength()
Return the length of a packet. This includes the header, so an empty packet is 11 bytes long.

        return mLength;
    
private static synchronized intgetNextSerial()
Get the next serial number. This creates a unique serial number across all connections, not just for the current connection. This is a useful property when debugging, but isn't necessary. We can't synchronize on an int, so we use a sync method.

        return mSerialId++;
    
java.nio.ByteBuffergetPayload()
Return a slice of the byte buffer, positioned past the JDWP header to the start of the chunk header. The buffer's limit will be set to the size of the payload if the size is known; if this is a packet under construction the limit will be set to the end of the buffer. Doesn't examine the packet at all -- works on empty buffers.

        ByteBuffer buf;
        int oldPosn = mBuffer.position();

        mBuffer.position(JDWP_HEADER_LEN);
        buf = mBuffer.slice();     // goes from position to limit
        mBuffer.position(oldPosn);

        if (mLength > 0)
            buf.limit(mLength - JDWP_HEADER_LEN);
        else
            assert mIsNew;
        buf.order(ChunkHandler.CHUNK_ORDER);
        return buf;
    
booleanisDdmPacket()
Returns "true" if this JDWP packet has a JDWP command type. This never returns "true" for reply packets.

        return (mFlags & REPLY_PACKET) == 0 &&
               mCmdSet == DDMS_CMD_SET &&
               mCmd == DDMS_CMD;
    
booleanisEmpty()
Returns "true" if this JDWP packet has no data.

        return (mLength == JDWP_HEADER_LEN);
    
booleanisError()
Returns "true" if this JDWP packet is a reply with a nonzero error code.

        return isReply() && mErrCode != 0;
    
booleanisReply()
Returns "true" if this JDWP packet is tagged as a reply.

        return (mFlags & REPLY_PACKET) != 0;
    
voidmovePacket(java.nio.ByteBuffer buf)
"Move" the packet data out of the buffer we're sitting on and into buf at the current position.

        Log.v("ddms", "moving " + mLength + " bytes");
        int oldPosn = mBuffer.position();

        mBuffer.position(0);
        mBuffer.limit(mLength);
        buf.put(mBuffer);
        mBuffer.position(mLength);
        mBuffer.limit(oldPosn);
        mBuffer.compact();      // shift posn...limit, posn<-pending data
    
static voidputHandshake(java.nio.ByteBuffer buf)
Copy the handshake string into the output buffer. On exit, "buf"s position will be advanced.

        buf.put(mHandshake);
    
voidwriteAndConsume(java.nio.channels.SocketChannel chan)
Write our packet to "chan". Consumes the packet as part of the write. The JDWP packet starts at offset 0 and ends at mBuffer.position().

        int oldLimit;

        //Log.i("ddms", "writeAndConsume: pos=" + mBuffer.position()
        //    + ", limit=" + mBuffer.limit());

        assert mLength > 0;

        mBuffer.flip();         // limit<-posn, posn<-0
        oldLimit = mBuffer.limit();
        mBuffer.limit(mLength);
        while (mBuffer.position() != mBuffer.limit()) {
            chan.write(mBuffer);
        }
        // position should now be at end of packet
        assert mBuffer.position() == mLength;

        mBuffer.limit(oldLimit);
        mBuffer.compact();      // shift posn...limit, posn<-pending data

        //Log.i("ddms", "               : pos=" + mBuffer.position()
        //    + ", limit=" + mBuffer.limit());