blob: 36e24a30ca6b2aaf5c7e50208487abf0d4c96b3c [file] [log] [blame]
/*
* Copyright (C) 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 com.android.ddmlib.DebugPortManager.IDebugPortProvider;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Subclass this with a class that handles one or more chunk types.
*/
abstract class ChunkHandler {
public static final int CHUNK_HEADER_LEN = 8; // 4-byte type, 4-byte len
public static final ByteOrder CHUNK_ORDER = ByteOrder.BIG_ENDIAN;
public static final int CHUNK_FAIL = type("FAIL");
ChunkHandler() {}
/**
* Client is ready. The monitor thread calls this method on all
* handlers when the client is determined to be DDM-aware (usually
* after receiving a HELO response.)
*
* The handler can use this opportunity to initialize client-side
* activity. Because there's a fair chance we'll want to send a
* message to the client, this method can throw an IOException.
*/
abstract void clientReady(Client client) throws IOException;
/**
* Client has gone away. Can be used to clean up any resources
* associated with this client connection.
*/
abstract void clientDisconnected(Client client);
/**
* Handle an incoming chunk. The data, of chunk type "type", begins
* at the start of "data" and continues to data.limit().
*
* If "isReply" is set, then "msgId" will be the ID of the request
* we sent to the client. Otherwise, it's the ID generated by the
* client for this event. Note that it's possible to receive chunks
* in reply packets for which we are not registered.
*
* The handler may not modify the contents of "data".
*/
abstract void handleChunk(Client client, int type,
ByteBuffer data, boolean isReply, int msgId);
/**
* Handle chunks not recognized by handlers. The handleChunk() method
* in sub-classes should call this if the chunk type isn't recognized.
*/
protected void handleUnknownChunk(Client client, int type,
ByteBuffer data, boolean isReply, int msgId) {
if (type == CHUNK_FAIL) {
int errorCode, msgLen;
String msg;
errorCode = data.getInt();
msgLen = data.getInt();
msg = ByteBufferUtil.getString(data, msgLen);
Log.w("ddms", "WARNING: failure code=" + errorCode + " msg=" + msg);
} else {
Log.w("ddms", "WARNING: received unknown chunk " + name(type)
+ ": len=" + data.limit() + ", reply=" + isReply
+ ", msgId=0x" + Integer.toHexString(msgId));
}
Log.w("ddms", " client " + client + ", handler " + this);
}
/**
* Utility function to copy a String out of a ByteBuffer.
*/
public static String getString(ByteBuffer buf, int len) {
return ByteBufferUtil.getString(buf, len);
}
/**
* Convert a 4-character string to a 32-bit type.
*/
static int type(String typeName) {
int val = 0;
if (typeName.length() != 4) {
Log.e("ddms", "Type name must be 4 letter long");
throw new RuntimeException("Type name must be 4 letter long");
}
for (int i = 0; i < 4; i++) {
val <<= 8;
val |= (byte) typeName.charAt(i);
}
return val;
}
/**
* Convert an integer type to a 4-character string.
*/
static String name(int type) {
char[] ascii = new char[4];
ascii[0] = (char) ((type >> 24) & 0xff);
ascii[1] = (char) ((type >> 16) & 0xff);
ascii[2] = (char) ((type >> 8) & 0xff);
ascii[3] = (char) (type & 0xff);
return new String(ascii);
}
/**
* Allocate a ByteBuffer with enough space to hold the JDWP packet
* header and one chunk header in addition to the demands of the
* chunk being created.
*
* "maxChunkLen" indicates the size of the chunk contents only.
*/
static ByteBuffer allocBuffer(int maxChunkLen) {
ByteBuffer buf =
ByteBuffer.allocate(JdwpPacket.JDWP_HEADER_LEN + 8 +maxChunkLen);
buf.order(CHUNK_ORDER);
return buf;
}
/**
* Return the slice of the JDWP packet buffer that holds just the
* chunk data.
*/
static ByteBuffer getChunkDataBuf(ByteBuffer jdwpBuf) {
ByteBuffer slice;
assert jdwpBuf.position() == 0;
jdwpBuf.position(JdwpPacket.JDWP_HEADER_LEN + CHUNK_HEADER_LEN);
slice = jdwpBuf.slice();
slice.order(CHUNK_ORDER);
jdwpBuf.position(0);
return slice;
}
/**
* Write the chunk header at the start of the chunk.
*
* Pass in the byte buffer returned by JdwpPacket.getPayload().
*/
static void finishChunkPacket(JdwpPacket packet, int type, int chunkLen) {
ByteBuffer buf = packet.getPayload();
buf.putInt(0x00, type);
buf.putInt(0x04, chunkLen);
packet.finishPacket(CHUNK_HEADER_LEN + chunkLen);
}
/**
* Check that the client is opened with the proper debugger port for the
* specified application name, and if not, reopen it.
* @param client
* @param uiThread
* @param appName
* @return
*/
protected static Client checkDebuggerPortForAppName(Client client, String appName) {
IDebugPortProvider provider = DebugPortManager.getProvider();
if (provider != null) {
Device device = client.getDeviceImpl();
int newPort = provider.getPort(device, appName);
if (newPort != IDebugPortProvider.NO_STATIC_PORT &&
newPort != client.getDebuggerListenPort()) {
AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
if (bridge != null) {
DeviceMonitor deviceMonitor = bridge.getDeviceMonitor();
if (deviceMonitor != null) {
deviceMonitor.addClientToDropAndReopen(client, newPort);
client = null;
}
}
}
}
return client;
}
}