1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ddmlib; 18 19 import com.android.ddmlib.DebugPortManager.IDebugPortProvider; 20 21 import java.io.IOException; 22 import java.nio.ByteBuffer; 23 import java.nio.ByteOrder; 24 25 /** 26 * Subclass this with a class that handles one or more chunk types. 27 */ 28 abstract class ChunkHandler { 29 30 public static final int CHUNK_HEADER_LEN = 8; // 4-byte type, 4-byte len 31 public static final ByteOrder CHUNK_ORDER = ByteOrder.BIG_ENDIAN; 32 33 public static final int CHUNK_FAIL = type("FAIL"); 34 ChunkHandler()35 ChunkHandler() {} 36 37 /** 38 * Client is ready. The monitor thread calls this method on all 39 * handlers when the client is determined to be DDM-aware (usually 40 * after receiving a HELO response.) 41 * 42 * The handler can use this opportunity to initialize client-side 43 * activity. Because there's a fair chance we'll want to send a 44 * message to the client, this method can throw an IOException. 45 */ clientReady(Client client)46 abstract void clientReady(Client client) throws IOException; 47 48 /** 49 * Client has gone away. Can be used to clean up any resources 50 * associated with this client connection. 51 */ clientDisconnected(Client client)52 abstract void clientDisconnected(Client client); 53 54 /** 55 * Handle an incoming chunk. The data, of chunk type "type", begins 56 * at the start of "data" and continues to data.limit(). 57 * 58 * If "isReply" is set, then "msgId" will be the ID of the request 59 * we sent to the client. Otherwise, it's the ID generated by the 60 * client for this event. Note that it's possible to receive chunks 61 * in reply packets for which we are not registered. 62 * 63 * The handler may not modify the contents of "data". 64 */ handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId)65 abstract void handleChunk(Client client, int type, 66 ByteBuffer data, boolean isReply, int msgId); 67 68 /** 69 * Handle chunks not recognized by handlers. The handleChunk() method 70 * in sub-classes should call this if the chunk type isn't recognized. 71 */ handleUnknownChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId)72 protected void handleUnknownChunk(Client client, int type, 73 ByteBuffer data, boolean isReply, int msgId) { 74 if (type == CHUNK_FAIL) { 75 int errorCode, msgLen; 76 String msg; 77 78 errorCode = data.getInt(); 79 msgLen = data.getInt(); 80 msg = getString(data, msgLen); 81 Log.w("ddms", "WARNING: failure code=" + errorCode + " msg=" + msg); 82 } else { 83 Log.w("ddms", "WARNING: received unknown chunk " + name(type) 84 + ": len=" + data.limit() + ", reply=" + isReply 85 + ", msgId=0x" + Integer.toHexString(msgId)); 86 } 87 Log.w("ddms", " client " + client + ", handler " + this); 88 } 89 90 91 /** 92 * Utility function to copy a String out of a ByteBuffer. 93 * 94 * This is here because multiple chunk handlers can make use of it, 95 * and there's nowhere better to put it. 96 */ getString(ByteBuffer buf, int len)97 static String getString(ByteBuffer buf, int len) { 98 char[] data = new char[len]; 99 for (int i = 0; i < len; i++) 100 data[i] = buf.getChar(); 101 return new String(data); 102 } 103 104 /** 105 * Utility function to copy a String into a ByteBuffer. 106 */ putString(ByteBuffer buf, String str)107 static void putString(ByteBuffer buf, String str) { 108 int len = str.length(); 109 for (int i = 0; i < len; i++) 110 buf.putChar(str.charAt(i)); 111 } 112 113 /** 114 * Convert a 4-character string to a 32-bit type. 115 */ type(String typeName)116 static int type(String typeName) { 117 int val = 0; 118 119 if (typeName.length() != 4) { 120 Log.e("ddms", "Type name must be 4 letter long"); 121 throw new RuntimeException("Type name must be 4 letter long"); 122 } 123 124 for (int i = 0; i < 4; i++) { 125 val <<= 8; 126 val |= (byte) typeName.charAt(i); 127 } 128 129 return val; 130 } 131 132 /** 133 * Convert an integer type to a 4-character string. 134 */ name(int type)135 static String name(int type) { 136 char[] ascii = new char[4]; 137 138 ascii[0] = (char) ((type >> 24) & 0xff); 139 ascii[1] = (char) ((type >> 16) & 0xff); 140 ascii[2] = (char) ((type >> 8) & 0xff); 141 ascii[3] = (char) (type & 0xff); 142 143 return new String(ascii); 144 } 145 146 /** 147 * Allocate a ByteBuffer with enough space to hold the JDWP packet 148 * header and one chunk header in addition to the demands of the 149 * chunk being created. 150 * 151 * "maxChunkLen" indicates the size of the chunk contents only. 152 */ allocBuffer(int maxChunkLen)153 static ByteBuffer allocBuffer(int maxChunkLen) { 154 ByteBuffer buf = 155 ByteBuffer.allocate(JdwpPacket.JDWP_HEADER_LEN + 8 +maxChunkLen); 156 buf.order(CHUNK_ORDER); 157 return buf; 158 } 159 160 /** 161 * Return the slice of the JDWP packet buffer that holds just the 162 * chunk data. 163 */ getChunkDataBuf(ByteBuffer jdwpBuf)164 static ByteBuffer getChunkDataBuf(ByteBuffer jdwpBuf) { 165 ByteBuffer slice; 166 167 assert jdwpBuf.position() == 0; 168 169 jdwpBuf.position(JdwpPacket.JDWP_HEADER_LEN + CHUNK_HEADER_LEN); 170 slice = jdwpBuf.slice(); 171 slice.order(CHUNK_ORDER); 172 jdwpBuf.position(0); 173 174 return slice; 175 } 176 177 /** 178 * Write the chunk header at the start of the chunk. 179 * 180 * Pass in the byte buffer returned by JdwpPacket.getPayload(). 181 */ finishChunkPacket(JdwpPacket packet, int type, int chunkLen)182 static void finishChunkPacket(JdwpPacket packet, int type, int chunkLen) { 183 ByteBuffer buf = packet.getPayload(); 184 185 buf.putInt(0x00, type); 186 buf.putInt(0x04, chunkLen); 187 188 packet.finishPacket(CHUNK_HEADER_LEN + chunkLen); 189 } 190 191 /** 192 * Check that the client is opened with the proper debugger port for the 193 * specified application name, and if not, reopen it. 194 * @param client 195 * @param uiThread 196 * @param appName 197 * @return 198 */ checkDebuggerPortForAppName(Client client, String appName)199 protected static Client checkDebuggerPortForAppName(Client client, String appName) { 200 IDebugPortProvider provider = DebugPortManager.getProvider(); 201 if (provider != null) { 202 Device device = client.getDeviceImpl(); 203 int newPort = provider.getPort(device, appName); 204 205 if (newPort != IDebugPortProvider.NO_STATIC_PORT && 206 newPort != client.getDebuggerListenPort()) { 207 208 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge(); 209 if (bridge != null) { 210 DeviceMonitor deviceMonitor = bridge.getDeviceMonitor(); 211 if (deviceMonitor != null) { 212 deviceMonitor.addClientToDropAndReopen(client, newPort); 213 client = null; 214 } 215 } 216 } 217 } 218 219 return client; 220 } 221 } 222 223