Home | History | Annotate | Download | only in ddmlib
      1 /* //device/tools/ddms/libs/ddmlib/src/com/android/ddmlib/JdwpPacket.java
      2 **
      3 ** Copyright 2007, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 package com.android.ddmlib;
     19 
     20 import java.io.IOException;
     21 import java.nio.ByteBuffer;
     22 import java.nio.ByteOrder;
     23 import java.nio.channels.SocketChannel;
     24 
     25 /**
     26  * A JDWP packet, sitting at the start of a ByteBuffer somewhere.
     27  *
     28  * This allows us to wrap a "pointer" to the data with the results of
     29  * decoding the packet.
     30  *
     31  * None of the operations here are synchronized.  If multiple threads will
     32  * be accessing the same ByteBuffers, external sync will be required.
     33  *
     34  * Use the constructor to create an empty packet, or "findPacket()" to
     35  * wrap a JdwpPacket around existing data.
     36  */
     37 final class JdwpPacket {
     38     // header len
     39     public static final int JDWP_HEADER_LEN = 11;
     40 
     41     // results from findHandshake
     42     public static final int HANDSHAKE_GOOD = 1;
     43     public static final int HANDSHAKE_NOTYET = 2;
     44     public static final int HANDSHAKE_BAD = 3;
     45 
     46     // our cmdSet/cmd
     47     private static final int DDMS_CMD_SET = 0xc7;       // 'G' + 128
     48     private static final int DDMS_CMD = 0x01;
     49 
     50     // "flags" field
     51     private static final int REPLY_PACKET = 0x80;
     52 
     53     // this is sent and expected at the start of a JDWP connection
     54     private static final byte[] mHandshake = {
     55         'J', 'D', 'W', 'P', '-', 'H', 'a', 'n', 'd', 's', 'h', 'a', 'k', 'e'
     56     };
     57 
     58     public static final int HANDSHAKE_LEN = mHandshake.length;
     59 
     60     private ByteBuffer mBuffer;
     61     private int mLength, mId, mFlags, mCmdSet, mCmd, mErrCode;
     62     private boolean mIsNew;
     63 
     64     private static int mSerialId = 0x40000000;
     65 
     66 
     67     /**
     68      * Create a new, empty packet, in "buf".
     69      */
     70     JdwpPacket(ByteBuffer buf) {
     71         mBuffer = buf;
     72         mIsNew = true;
     73     }
     74 
     75     /**
     76      * Finish a packet created with newPacket().
     77      *
     78      * This always creates a command packet, with the next serial number
     79      * in sequence.
     80      *
     81      * We have to take "payloadLength" as an argument because we can't
     82      * see the position in the "slice" returned by getPayload().  We could
     83      * fish it out of the chunk header, but it's legal for there to be
     84      * more than one chunk in a JDWP packet.
     85      *
     86      * On exit, "position" points to the end of the data.
     87      */
     88     void finishPacket(int payloadLength) {
     89         assert mIsNew;
     90 
     91         ByteOrder oldOrder = mBuffer.order();
     92         mBuffer.order(ChunkHandler.CHUNK_ORDER);
     93 
     94         mLength = JDWP_HEADER_LEN + payloadLength;
     95         mId = getNextSerial();
     96         mFlags = 0;
     97         mCmdSet = DDMS_CMD_SET;
     98         mCmd = DDMS_CMD;
     99 
    100         mBuffer.putInt(0x00, mLength);
    101         mBuffer.putInt(0x04, mId);
    102         mBuffer.put(0x08, (byte) mFlags);
    103         mBuffer.put(0x09, (byte) mCmdSet);
    104         mBuffer.put(0x0a, (byte) mCmd);
    105 
    106         mBuffer.order(oldOrder);
    107         mBuffer.position(mLength);
    108     }
    109 
    110     /**
    111      * Get the next serial number.  This creates a unique serial number
    112      * across all connections, not just for the current connection.  This
    113      * is a useful property when debugging, but isn't necessary.
    114      *
    115      * We can't synchronize on an int, so we use a sync method.
    116      */
    117     private static synchronized int getNextSerial() {
    118         return mSerialId++;
    119     }
    120 
    121     /**
    122      * Return a slice of the byte buffer, positioned past the JDWP header
    123      * to the start of the chunk header.  The buffer's limit will be set
    124      * to the size of the payload if the size is known; if this is a
    125      * packet under construction the limit will be set to the end of the
    126      * buffer.
    127      *
    128      * Doesn't examine the packet at all -- works on empty buffers.
    129      */
    130     ByteBuffer getPayload() {
    131         ByteBuffer buf;
    132         int oldPosn = mBuffer.position();
    133 
    134         mBuffer.position(JDWP_HEADER_LEN);
    135         buf = mBuffer.slice();     // goes from position to limit
    136         mBuffer.position(oldPosn);
    137 
    138         if (mLength > 0)
    139             buf.limit(mLength - JDWP_HEADER_LEN);
    140         else
    141             assert mIsNew;
    142         buf.order(ChunkHandler.CHUNK_ORDER);
    143         return buf;
    144     }
    145 
    146     /**
    147      * Returns "true" if this JDWP packet has a JDWP command type.
    148      *
    149      * This never returns "true" for reply packets.
    150      */
    151     boolean isDdmPacket() {
    152         return (mFlags & REPLY_PACKET) == 0 &&
    153                mCmdSet == DDMS_CMD_SET &&
    154                mCmd == DDMS_CMD;
    155     }
    156 
    157     /**
    158      * Returns "true" if this JDWP packet is tagged as a reply.
    159      */
    160     boolean isReply() {
    161         return (mFlags & REPLY_PACKET) != 0;
    162     }
    163 
    164     /**
    165      * Returns "true" if this JDWP packet is a reply with a nonzero
    166      * error code.
    167      */
    168     boolean isError() {
    169         return isReply() && mErrCode != 0;
    170     }
    171 
    172     /**
    173      * Returns "true" if this JDWP packet has no data.
    174      */
    175     boolean isEmpty() {
    176         return (mLength == JDWP_HEADER_LEN);
    177     }
    178 
    179     /**
    180      * Return the packet's ID.  For a reply packet, this allows us to
    181      * match the reply with the original request.
    182      */
    183     int getId() {
    184         return mId;
    185     }
    186 
    187     /**
    188      * Return the length of a packet.  This includes the header, so an
    189      * empty packet is 11 bytes long.
    190      */
    191     int getLength() {
    192         return mLength;
    193     }
    194 
    195     /**
    196      * Write our packet to "chan".  Consumes the packet as part of the
    197      * write.
    198      *
    199      * The JDWP packet starts at offset 0 and ends at mBuffer.position().
    200      */
    201     void writeAndConsume(SocketChannel chan) throws IOException {
    202         int oldLimit;
    203 
    204         //Log.i("ddms", "writeAndConsume: pos=" + mBuffer.position()
    205         //    + ", limit=" + mBuffer.limit());
    206 
    207         assert mLength > 0;
    208 
    209         mBuffer.flip();         // limit<-posn, posn<-0
    210         oldLimit = mBuffer.limit();
    211         mBuffer.limit(mLength);
    212         while (mBuffer.position() != mBuffer.limit()) {
    213             chan.write(mBuffer);
    214         }
    215         // position should now be at end of packet
    216         assert mBuffer.position() == mLength;
    217 
    218         mBuffer.limit(oldLimit);
    219         mBuffer.compact();      // shift posn...limit, posn<-pending data
    220 
    221         //Log.i("ddms", "               : pos=" + mBuffer.position()
    222         //    + ", limit=" + mBuffer.limit());
    223     }
    224 
    225     /**
    226      * "Move" the packet data out of the buffer we're sitting on and into
    227      * buf at the current position.
    228      */
    229     void movePacket(ByteBuffer buf) {
    230         Log.v("ddms", "moving " + mLength + " bytes");
    231         int oldPosn = mBuffer.position();
    232 
    233         mBuffer.position(0);
    234         mBuffer.limit(mLength);
    235         buf.put(mBuffer);
    236         mBuffer.position(mLength);
    237         mBuffer.limit(oldPosn);
    238         mBuffer.compact();      // shift posn...limit, posn<-pending data
    239     }
    240 
    241     /**
    242      * Consume the JDWP packet.
    243      *
    244      * On entry and exit, "position" is the #of bytes in the buffer.
    245      */
    246     void consume()
    247     {
    248         //Log.d("ddms", "consuming " + mLength + " bytes");
    249         //Log.d("ddms", "  posn=" + mBuffer.position()
    250         //    + ", limit=" + mBuffer.limit());
    251 
    252         /*
    253          * The "flip" call sets "limit" equal to the position (usually the
    254          * end of data) and "position" equal to zero.
    255          *
    256          * compact() copies everything from "position" and "limit" to the
    257          * start of the buffer, sets "position" to the end of data, and
    258          * sets "limit" to the capacity.
    259          *
    260          * On entry, "position" is set to the amount of data in the buffer
    261          * and "limit" is set to the capacity.  We want to call flip()
    262          * so that position..limit spans our data, advance "position" past
    263          * the current packet, then compact.
    264          */
    265         mBuffer.flip();         // limit<-posn, posn<-0
    266         mBuffer.position(mLength);
    267         mBuffer.compact();      // shift posn...limit, posn<-pending data
    268         mLength = 0;
    269         //Log.d("ddms", "  after compact, posn=" + mBuffer.position()
    270         //    + ", limit=" + mBuffer.limit());
    271     }
    272 
    273     /**
    274      * Find the JDWP packet at the start of "buf".  The start is known,
    275      * but the length has to be parsed out.
    276      *
    277      * On entry, the packet data in "buf" must start at offset 0 and end
    278      * at "position".  "limit" should be set to the buffer capacity.  This
    279      * method does not alter "buf"s attributes.
    280      *
    281      * Returns a new JdwpPacket if a full one is found in the buffer.  If
    282      * not, returns null.  Throws an exception if the data doesn't look like
    283      * a valid JDWP packet.
    284      */
    285     static JdwpPacket findPacket(ByteBuffer buf) {
    286         int count = buf.position();
    287         int length, id, flags, cmdSet, cmd;
    288 
    289         if (count < JDWP_HEADER_LEN)
    290             return null;
    291 
    292         ByteOrder oldOrder = buf.order();
    293         buf.order(ChunkHandler.CHUNK_ORDER);
    294 
    295         length = buf.getInt(0x00);
    296         id = buf.getInt(0x04);
    297         flags = buf.get(0x08) & 0xff;
    298         cmdSet = buf.get(0x09) & 0xff;
    299         cmd = buf.get(0x0a) & 0xff;
    300 
    301         buf.order(oldOrder);
    302 
    303         if (length < JDWP_HEADER_LEN)
    304             throw new BadPacketException();
    305         if (count < length)
    306             return null;
    307 
    308         JdwpPacket pkt = new JdwpPacket(buf);
    309         //pkt.mBuffer = buf;
    310         pkt.mLength = length;
    311         pkt.mId = id;
    312         pkt.mFlags = flags;
    313 
    314         if ((flags & REPLY_PACKET) == 0) {
    315             pkt.mCmdSet = cmdSet;
    316             pkt.mCmd = cmd;
    317             pkt.mErrCode = -1;
    318         } else {
    319             pkt.mCmdSet = -1;
    320             pkt.mCmd = -1;
    321             pkt.mErrCode = cmdSet | (cmd << 8);
    322         }
    323 
    324         return pkt;
    325     }
    326 
    327     /**
    328      * Like findPacket(), but when we're expecting the JDWP handshake.
    329      *
    330      * Returns one of:
    331      *   HANDSHAKE_GOOD   - found handshake, looks good
    332      *   HANDSHAKE_BAD    - found enough data, but it's wrong
    333      *   HANDSHAKE_NOTYET - not enough data has been read yet
    334      */
    335     static int findHandshake(ByteBuffer buf) {
    336         int count = buf.position();
    337         int i;
    338 
    339         if (count < mHandshake.length)
    340             return HANDSHAKE_NOTYET;
    341 
    342         for (i = mHandshake.length -1; i >= 0; --i) {
    343             if (buf.get(i) != mHandshake[i])
    344                 return HANDSHAKE_BAD;
    345         }
    346 
    347         return HANDSHAKE_GOOD;
    348     }
    349 
    350     /**
    351      * Remove the handshake string from the buffer.
    352      *
    353      * On entry and exit, "position" is the #of bytes in the buffer.
    354      */
    355     static void consumeHandshake(ByteBuffer buf) {
    356         // in theory, nothing else can have arrived, so this is overkill
    357         buf.flip();         // limit<-posn, posn<-0
    358         buf.position(mHandshake.length);
    359         buf.compact();      // shift posn...limit, posn<-pending data
    360     }
    361 
    362     /**
    363      * Copy the handshake string into the output buffer.
    364      *
    365      * On exit, "buf"s position will be advanced.
    366      */
    367     static void putHandshake(ByteBuffer buf) {
    368         buf.put(mHandshake);
    369     }
    370 }
    371 
    372