![JAR search and dependency download from the Maven repository](/logo.png)
com.android.ddmlib.ChunkHandler Maven / Gradle / Ivy
/*
* 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.annotations.NonNull;
import com.android.ddmlib.DebugPortManager.IDebugPortProvider;
import com.android.ddmlib.jdwp.JdwpAgent;
import com.android.ddmlib.jdwp.JdwpInterceptor;
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 extends JdwpInterceptor {
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");
public static final int DDMS_CMD_SET = 0xc7; // 'G' + 128
public static final int DDMS_CMD = 0x01;
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(DDMS_CMD_SET, DDMS_CMD, 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 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;
}
void handlePacket(Client client, JdwpPacket packet) {
ByteBuffer buf = packet.getPayload();
int type = buf.getInt();
int length = buf.getInt();
Log.d("ddms", "Calling handler for " + name(type)
+ " [" + this + "] (len=" + length + ")");
ByteBuffer ibuf = buf.slice();
ByteBuffer roBuf = ibuf.asReadOnlyBuffer(); // enforce R/O
roBuf.order(CHUNK_ORDER);
handleChunk(client, type, roBuf, packet.isReply(), packet.getId());
}
@Override
public JdwpPacket intercept(@NonNull JdwpAgent agent, @NonNull JdwpPacket packet) {
// TODO: ChunkHandlers are specific to client only packages. Further refactoring
// is needed to properly generalize them to JdwpInterceptors
if (agent instanceof Client) {
Client client = (Client)agent;
// TODO: ChunkHandlers are currently all static objects created in static
// initializers. For many different reasons they should not be there and should
// be moved to another creation mechanism where they are part of the ddm extension
// workflow. For now, access the ddmextension directly.
MonitorThread.getInstance().getDdmExtension().ddmSeen(client);
if (packet.isError()) {
client.packetFailed(packet);
} else if (packet.isEmpty()) {
Log.d("ddms", "Got empty reply for 0x" + Integer.toHexString(packet.getId()));
} else {
handlePacket(client, packet);
}
return null;
}
return packet;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy