blob: 4378350f1f73f3815ec42f6112b736e7e745f23e [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.annotations.NonNull;
import com.android.annotations.Nullable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public final class HandleViewDebug extends ChunkHandler {
/** Enable/Disable tracing of OpenGL calls. */
public static final int CHUNK_VUGL = type("VUGL");
/** List {@link ViewRootImpl}'s of this process. */
public static final int CHUNK_VULW = type("VULW");
/** Operation on view root, first parameter in packet should be one of VURT_* constants */
public static final int CHUNK_VURT = type("VURT");
/** Dump view hierarchy. */
private static final int VURT_DUMP_HIERARCHY = 1;
/** Capture View Layers. */
private static final int VURT_CAPTURE_LAYERS = 2;
/** Dump View Theme. */
private static final int VURT_DUMP_THEME = 3;
/**
* Generic View Operation, first parameter in the packet should be one of the
* VUOP_* constants below.
*/
public static final int CHUNK_VUOP = type("VUOP");
/** Capture View. */
private static final int VUOP_CAPTURE_VIEW = 1;
/** Obtain the Display List corresponding to the view. */
private static final int VUOP_DUMP_DISPLAYLIST = 2;
/** Profile a view. */
private static final int VUOP_PROFILE_VIEW = 3;
/** Invoke a method on the view. */
private static final int VUOP_INVOKE_VIEW_METHOD = 4;
/** Set layout parameter. */
private static final int VUOP_SET_LAYOUT_PARAMETER = 5;
private static final String TAG = "ddmlib"; //$NON-NLS-1$
private static final HandleViewDebug sInstance = new HandleViewDebug();
private static final ViewDumpHandler sViewOpNullChunkHandler =
new NullChunkHandler(CHUNK_VUOP);
private HandleViewDebug() {}
public static void register(MonitorThread mt) {
// TODO: add chunk type for auto window updates
// and register here
mt.registerChunkHandler(CHUNK_VUGL, sInstance);
mt.registerChunkHandler(CHUNK_VULW, sInstance);
mt.registerChunkHandler(CHUNK_VUOP, sInstance);
mt.registerChunkHandler(CHUNK_VURT, sInstance);
}
@Override
public void clientReady(Client client) throws IOException {}
@Override
public void clientDisconnected(Client client) {}
public abstract static class ViewDumpHandler extends ChunkHandler {
private final CountDownLatch mLatch = new CountDownLatch(1);
private final int mChunkType;
public ViewDumpHandler(int chunkType) {
mChunkType = chunkType;
}
@Override
void clientReady(Client client) throws IOException {
}
@Override
void clientDisconnected(Client client) {
}
@Override
void handleChunk(Client client, int type, ByteBuffer data,
boolean isReply, int msgId) {
if (type != mChunkType) {
handleUnknownChunk(client, type, data, isReply, msgId);
return;
}
handleViewDebugResult(data);
mLatch.countDown();
}
protected abstract void handleViewDebugResult(ByteBuffer data);
protected void waitForResult(long timeout, TimeUnit unit) {
try {
mLatch.await(timeout, unit);
} catch (InterruptedException e) {
// pass
}
}
}
public static void listViewRoots(Client client, ViewDumpHandler replyHandler)
throws IOException {
ByteBuffer buf = allocBuffer(8);
JdwpPacket packet = new JdwpPacket(buf);
ByteBuffer chunkBuf = getChunkDataBuf(buf);
chunkBuf.putInt(1);
finishChunkPacket(packet, CHUNK_VULW, chunkBuf.position());
client.sendAndConsume(packet, replyHandler);
}
public static void dumpViewHierarchy(@NonNull Client client, @NonNull String viewRoot,
boolean skipChildren, boolean includeProperties, @NonNull ViewDumpHandler handler)
throws IOException {
ByteBuffer buf = allocBuffer(4 // opcode
+ 4 // view root length
+ viewRoot.length() * 2 // view root
+ 4 // skip children
+ 4); // include view properties
JdwpPacket packet = new JdwpPacket(buf);
ByteBuffer chunkBuf = getChunkDataBuf(buf);
chunkBuf.putInt(VURT_DUMP_HIERARCHY);
chunkBuf.putInt(viewRoot.length());
ByteBufferUtil.putString(chunkBuf, viewRoot);
chunkBuf.putInt(skipChildren ? 1 : 0);
chunkBuf.putInt(includeProperties ? 1 : 0);
finishChunkPacket(packet, CHUNK_VURT, chunkBuf.position());
client.sendAndConsume(packet, handler);
}
public static void captureLayers(@NonNull Client client, @NonNull String viewRoot,
@NonNull ViewDumpHandler handler) throws IOException {
int bufLen = 8 + viewRoot.length() * 2;
ByteBuffer buf = allocBuffer(bufLen);
JdwpPacket packet = new JdwpPacket(buf);
ByteBuffer chunkBuf = getChunkDataBuf(buf);
chunkBuf.putInt(VURT_CAPTURE_LAYERS);
chunkBuf.putInt(viewRoot.length());
ByteBufferUtil.putString(chunkBuf, viewRoot);
finishChunkPacket(packet, CHUNK_VURT, chunkBuf.position());
client.sendAndConsume(packet, handler);
}
private static void sendViewOpPacket(@NonNull Client client, int op, @NonNull String viewRoot,
@NonNull String view, @Nullable byte[] extra, @Nullable ViewDumpHandler handler)
throws IOException {
int bufLen = 4 + // opcode
4 + viewRoot.length() * 2 + // view root strlen + view root
4 + view.length() * 2; // view strlen + view
if (extra != null) {
bufLen += extra.length;
}
ByteBuffer buf = allocBuffer(bufLen);
JdwpPacket packet = new JdwpPacket(buf);
ByteBuffer chunkBuf = getChunkDataBuf(buf);
chunkBuf.putInt(op);
chunkBuf.putInt(viewRoot.length());
ByteBufferUtil.putString(chunkBuf, viewRoot);
chunkBuf.putInt(view.length());
ByteBufferUtil.putString(chunkBuf, view);
if (extra != null) {
chunkBuf.put(extra);
}
finishChunkPacket(packet, CHUNK_VUOP, chunkBuf.position());
if (handler != null) {
client.sendAndConsume(packet, handler);
} else {
client.sendAndConsume(packet);
}
}
public static void profileView(@NonNull Client client, @NonNull String viewRoot,
@NonNull String view, @NonNull ViewDumpHandler handler) throws IOException {
sendViewOpPacket(client, VUOP_PROFILE_VIEW, viewRoot, view, null, handler);
}
public static void captureView(@NonNull Client client, @NonNull String viewRoot,
@NonNull String view, @NonNull ViewDumpHandler handler) throws IOException {
sendViewOpPacket(client, VUOP_CAPTURE_VIEW, viewRoot, view, null, handler);
}
public static void invalidateView(@NonNull Client client, @NonNull String viewRoot,
@NonNull String view) throws IOException {
invokeMethod(client, viewRoot, view, "invalidate");
}
public static void requestLayout(@NonNull Client client, @NonNull String viewRoot,
@NonNull String view) throws IOException {
invokeMethod(client, viewRoot, view, "requestLayout");
}
public static void dumpDisplayList(@NonNull Client client, @NonNull String viewRoot,
@NonNull String view) throws IOException {
sendViewOpPacket(client, VUOP_DUMP_DISPLAYLIST, viewRoot, view, null,
sViewOpNullChunkHandler);
}
public static void dumpTheme(@NonNull Client client, @NonNull String viewRoot,
@NonNull ViewDumpHandler handler)
throws IOException {
ByteBuffer buf = allocBuffer(4 // opcode
+ 4 // view root length
+ viewRoot.length() * 2); // view root
JdwpPacket packet = new JdwpPacket(buf);
ByteBuffer chunkBuf = getChunkDataBuf(buf);
chunkBuf.putInt(VURT_DUMP_THEME);
chunkBuf.putInt(viewRoot.length());
ByteBufferUtil.putString(chunkBuf, viewRoot);
finishChunkPacket(packet, CHUNK_VURT, chunkBuf.position());
client.sendAndConsume(packet, handler);
}
/** A {@link ViewDumpHandler} to use when no response is expected. */
private static class NullChunkHandler extends ViewDumpHandler {
public NullChunkHandler(int chunkType) {
super(chunkType);
}
@Override
protected void handleViewDebugResult(ByteBuffer data) {
}
}
public static void invokeMethod(@NonNull Client client, @NonNull String viewRoot,
@NonNull String view, @NonNull String method, Object... args) throws IOException {
int len = 4 + method.length() * 2;
if (args != null) {
// # of args
len += 4;
// for each argument, we send a char type specifier (2 bytes) and
// the arg value (max primitive size = sizeof(double) = 8
len += 10 * args.length;
}
byte[] extra = new byte[len];
ByteBuffer b = ByteBuffer.wrap(extra);
b.putInt(method.length());
ByteBufferUtil.putString(b, method);
if (args != null) {
b.putInt(args.length);
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg instanceof Boolean) {
b.putChar('Z');
b.put((byte) ((Boolean) arg ? 1 : 0));
} else if (arg instanceof Byte) {
b.putChar('B');
b.put((Byte) arg);
} else if (arg instanceof Character) {
b.putChar('C');
b.putChar((Character) arg);
} else if (arg instanceof Short) {
b.putChar('S');
b.putShort((Short) arg);
} else if (arg instanceof Integer) {
b.putChar('I');
b.putInt((Integer) arg);
} else if (arg instanceof Long) {
b.putChar('J');
b.putLong((Long) arg);
} else if (arg instanceof Float) {
b.putChar('F');
b.putFloat((Float) arg);
} else if (arg instanceof Double) {
b.putChar('D');
b.putDouble((Double) arg);
} else {
Log.e(TAG, "View method invocation only supports primitive arguments, supplied: " + arg);
return;
}
}
}
sendViewOpPacket(client, VUOP_INVOKE_VIEW_METHOD, viewRoot, view, extra,
sViewOpNullChunkHandler );
}
public static void setLayoutParameter(@NonNull Client client, @NonNull String viewRoot,
@NonNull String view, @NonNull String parameter, int value) throws IOException {
int len = 4 + parameter.length() * 2 + 4;
byte[] extra = new byte[len];
ByteBuffer b = ByteBuffer.wrap(extra);
b.putInt(parameter.length());
ByteBufferUtil.putString(b, parameter);
b.putInt(value);
sendViewOpPacket(client, VUOP_SET_LAYOUT_PARAMETER, viewRoot, view, extra,
sViewOpNullChunkHandler);
}
@Override
public void handleChunk(Client client, int type, ByteBuffer data,
boolean isReply, int msgId) {
}
public static void sendStartGlTracing(Client client) throws IOException {
ByteBuffer buf = allocBuffer(4);
JdwpPacket packet = new JdwpPacket(buf);
ByteBuffer chunkBuf = getChunkDataBuf(buf);
chunkBuf.putInt(1);
finishChunkPacket(packet, CHUNK_VUGL, chunkBuf.position());
client.sendAndConsume(packet);
}
public static void sendStopGlTracing(Client client) throws IOException {
ByteBuffer buf = allocBuffer(4);
JdwpPacket packet = new JdwpPacket(buf);
ByteBuffer chunkBuf = getChunkDataBuf(buf);
chunkBuf.putInt(0);
finishChunkPacket(packet, CHUNK_VUGL, chunkBuf.position());
client.sendAndConsume(packet);
}
}