• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
JdwpPacket(ByteBuffer buf)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      */
finishPacket(int payloadLength)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      */
getNextSerial()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      */
getPayload()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      */
isDdmPacket()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      */
isReply()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      */
isError()168     boolean isError() {
169         return isReply() && mErrCode != 0;
170     }
171 
172     /**
173      * Returns "true" if this JDWP packet has no data.
174      */
isEmpty()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      */
getId()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      */
getLength()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      */
writeAndConsume(SocketChannel chan)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      */
movePacket(ByteBuffer buf)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      */
consume()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      */
findPacket(ByteBuffer buf)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      */
findHandshake(ByteBuffer buf)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      */
consumeHandshake(ByteBuffer buf)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      */
putHandshake(ByteBuffer buf)367     static void putHandshake(ByteBuffer buf) {
368         buf.put(mHandshake);
369     }
370 }
371 
372