
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.
*/
public final class JdwpPacket {
public static final int JDWP_HEADER_LEN = 11;
private static final int REPLY_PACKET = 0x80;
private ByteBuffer mBuffer;
private int mLength;
private int mId;
private int mFlags;
private int mCmdSet;
private int mCmd;
private int mErrCode;
private static int sSerialId = 0x40000000;
/**
* Create a new, empty packet, in "buf".
*/
JdwpPacket(ByteBuffer buf) {
mBuffer = buf;
}
/**
* 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 cmdSet, int cmd, int payloadLength) {
ByteOrder oldOrder = mBuffer.order();
mBuffer.order(ChunkHandler.CHUNK_ORDER);
mLength = JDWP_HEADER_LEN + payloadLength;
mId = getNextSerial();
mFlags = 0;
mCmdSet = cmdSet;
mCmd = 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.
*/
public 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);
buf.order(ChunkHandler.CHUNK_ORDER);
return buf;
}
/**
* Returns "true" if this JDWP packet is tagged as a reply.
*/
public 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.
*/
public 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".
*
* The JDWP packet starts at offset 0 and ends at mBuffer.position().
*/
void write(SocketChannel chan) throws IOException {
assert mLength > 0;
int oldPosn = mBuffer.position();
mBuffer.position(0);
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(mBuffer.capacity());
mBuffer.position(oldPosn);
}
/**
* "Move" the packet data out of the buffer we're sitting on and into
* buf at the current position.
*/
void move(ByteBuffer buf) {
int oldPosn = mBuffer.position();
mBuffer.position(0);
mBuffer.limit(mLength);
buf.put(mBuffer);
mBuffer.limit(mBuffer.capacity());
mBuffer.position(oldPosn);
}
/**
* Consume the JDWP packet.
*
* On entry and exit, "position" is at the end of data in buffer.
*/
void consume()
{
/*
* 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();
mBuffer.position(mLength);
mBuffer.compact();
mLength = 0;
}
/**
* 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;
}
@Override
public String toString() {
return isReply() ? " < # " + mId : " > " + mCmdSet + "." + mCmd + " # " + mId;
}
public boolean is(int cmdSet, int cmd) {
return cmdSet == mCmdSet && cmd == mCmd;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy