/* * 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); } }