com.android.ddmlib.JdwpPacket Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ddmlib Show documentation
Show all versions of ddmlib Show documentation
Library providing APIs to talk to Android devices
/* //device/tools/ddms/libs/ddmlib/src/com/android/ddmlib/JdwpPacket.java
**
** Copyright 2007, 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.
*/
package com.android.ddmlib;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.SocketChannel;
/**
* 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.
*/
final class JdwpPacket {
// header len
public static final int JDWP_HEADER_LEN = 11;
// results from findHandshake
public static final int HANDSHAKE_GOOD = 1;
public static final int HANDSHAKE_NOTYET = 2;
public static final int HANDSHAKE_BAD = 3;
// our cmdSet/cmd
private static final int DDMS_CMD_SET = 0xc7; // 'G' + 128
private static final int DDMS_CMD = 0x01;
// "flags" field
private static final int REPLY_PACKET = 0x80;
// this is sent and expected at the start of a JDWP connection
private static final byte[] mHandshake = {
'J', 'D', 'W', 'P', '-', 'H', 'a', 'n', 'd', 's', 'h', 'a', 'k', 'e'
};
public static final int HANDSHAKE_LEN = mHandshake.length;
private ByteBuffer mBuffer;
private int mLength, mId, mFlags, mCmdSet, mCmd, mErrCode;
private boolean mIsNew;
private static int sSerialId = 0x40000000;
/**
* Create a new, empty packet, in "buf".
*/
JdwpPacket(ByteBuffer buf) {
mBuffer = buf;
mIsNew = true;
}
/**
* 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.
*/
void finishPacket(int payloadLength) {
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);
}
/**
* 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.
*/
private static synchronized int getNextSerial() {
return sSerialId++;
}
/**
* 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 getPayload() {
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;
}
/**
* Returns "true" if this JDWP packet has a JDWP command type.
*
* This never returns "true" for reply packets.
*/
boolean isDdmPacket() {
return (mFlags & REPLY_PACKET) == 0 &&
mCmdSet == DDMS_CMD_SET &&
mCmd == DDMS_CMD;
}
/**
* Returns "true" if this JDWP packet is tagged as a reply.
*/
boolean isReply() {
return (mFlags & REPLY_PACKET) != 0;
}
/**
* Returns "true" if this JDWP packet is a reply with a nonzero
* error code.
*/
boolean isError() {
return isReply() && mErrCode != 0;
}
/**
* Returns "true" if this JDWP packet has no data.
*/
boolean isEmpty() {
return (mLength == JDWP_HEADER_LEN);
}
/**
* Return the packet's ID. For a reply packet, this allows us to
* match the reply with the original request.
*/
int getId() {
return mId;
}
/**
* Return the length of a packet. This includes the header, so an
* empty packet is 11 bytes long.
*/
int getLength() {
return mLength;
}
/**
* 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().
*/
void writeAndConsume(SocketChannel chan) throws IOException {
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());
}
/**
* "Move" the packet data out of the buffer we're sitting on and into
* buf at the current position.
*/
void movePacket(ByteBuffer buf) {
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
}
/**
* Consume the JDWP packet.
*
* On entry and exit, "position" is the #of bytes in the buffer.
*/
void consume()
{
//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());
}
/**
* 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.
*/
static JdwpPacket findPacket(ByteBuffer buf) {
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;
}
/**
* 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
*/
static int findHandshake(ByteBuffer buf) {
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;
}
/**
* Remove the handshake string from the buffer.
*
* On entry and exit, "position" is the #of bytes in the buffer.
*/
static void consumeHandshake(ByteBuffer buf) {
// 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
}
/**
* Copy the handshake string into the output buffer.
*
* On exit, "buf"s position will be advanced.
*/
static void putHandshake(ByteBuffer buf) {
buf.put(mHandshake);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy