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