summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorSiva Velusamy <vsiva@google.com>2013-01-16 22:23:39 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-01-16 22:23:55 +0000
commit281184fb86a1866b3e8f8a0d8b38a1aa934849dc (patch)
tree46c80d92b8dd16180b360c2c933bdf247aa0a161 /core
parent35654b61e8fe7bc85afcb076ddbb590d51c5865f (diff)
parent945bfb6068d4ac1414a37a3ebe4dc4d02383e38e (diff)
downloadframeworks_base-281184fb86a1866b3e8f8a0d8b38a1aa934849dc.zip
frameworks_base-281184fb86a1866b3e8f8a0d8b38a1aa934849dc.tar.gz
frameworks_base-281184fb86a1866b3e8f8a0d8b38a1aa934849dc.tar.bz2
Merge "Support hierarchy viewer commands via DDM"
Diffstat (limited to 'core')
-rw-r--r--core/java/android/ddm/DdmHandleGlTracing.java60
-rw-r--r--core/java/android/ddm/DdmHandleHello.java19
-rw-r--r--core/java/android/ddm/DdmHandleViewDebug.java315
-rw-r--r--core/java/android/ddm/DdmRegister.java2
-rw-r--r--core/java/android/view/ViewDebug.java55
-rw-r--r--core/java/android/view/WindowManagerGlobal.java23
6 files changed, 391 insertions, 83 deletions
diff --git a/core/java/android/ddm/DdmHandleGlTracing.java b/core/java/android/ddm/DdmHandleGlTracing.java
deleted file mode 100644
index 511cf44..0000000
--- a/core/java/android/ddm/DdmHandleGlTracing.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2012 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 android.ddm;
-
-import android.opengl.GLUtils;
-
-import org.apache.harmony.dalvik.ddmc.Chunk;
-import org.apache.harmony.dalvik.ddmc.ChunkHandler;
-import org.apache.harmony.dalvik.ddmc.DdmServer;
-
-import java.nio.ByteBuffer;
-
-public class DdmHandleGlTracing extends ChunkHandler {
- /** GL TRace control packets. Packet data controls starting/stopping the trace. */
- public static final int CHUNK_GLTR = type("GLTR");
-
- private static final DdmHandleGlTracing sInstance = new DdmHandleGlTracing();
-
- /** singleton, do not instantiate. */
- private DdmHandleGlTracing() {}
-
- public static void register() {
- DdmServer.registerHandler(CHUNK_GLTR, sInstance);
- }
-
- @Override
- public void connected() {
- }
-
- @Override
- public void disconnected() {
- }
-
- @Override
- public Chunk handleChunk(Chunk request) {
- int type = request.type;
-
- if (type != CHUNK_GLTR) {
- throw new RuntimeException("Unknown packet " + ChunkHandler.name(type));
- }
-
- ByteBuffer in = wrapChunk(request);
- GLUtils.setTracingLevel(in.getInt());
- return null; // empty response
- }
-}
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index 70ad648..842a482 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -36,7 +36,10 @@ public class DdmHandleHello extends ChunkHandler {
private static DdmHandleHello mInstance = new DdmHandleHello();
- private static final String[] NATIVE_FEATURES = new String[] { "opengl-tracing" };
+ private static final String[] FRAMEWORK_FEATURES = new String[] {
+ "opengl-tracing",
+ "view-hierarchy",
+ };
/* singleton, do not instantiate */
private DdmHandleHello() {}
@@ -155,22 +158,22 @@ public class DdmHandleHello extends ChunkHandler {
if (false)
Log.v("ddm-heap", "Got feature list request");
- int size = 4 + 4 * (vmFeatures.length + NATIVE_FEATURES.length);
+ int size = 4 + 4 * (vmFeatures.length + FRAMEWORK_FEATURES.length);
for (int i = vmFeatures.length-1; i >= 0; i--)
size += vmFeatures[i].length() * 2;
- for (int i = NATIVE_FEATURES.length-1; i>= 0; i--)
- size += NATIVE_FEATURES[i].length() * 2;
+ for (int i = FRAMEWORK_FEATURES.length-1; i>= 0; i--)
+ size += FRAMEWORK_FEATURES[i].length() * 2;
ByteBuffer out = ByteBuffer.allocate(size);
out.order(ChunkHandler.CHUNK_ORDER);
- out.putInt(vmFeatures.length + NATIVE_FEATURES.length);
+ out.putInt(vmFeatures.length + FRAMEWORK_FEATURES.length);
for (int i = vmFeatures.length-1; i >= 0; i--) {
out.putInt(vmFeatures[i].length());
putString(out, vmFeatures[i]);
}
- for (int i = NATIVE_FEATURES.length-1; i >= 0; i--) {
- out.putInt(NATIVE_FEATURES[i].length());
- putString(out, NATIVE_FEATURES[i]);
+ for (int i = FRAMEWORK_FEATURES.length-1; i >= 0; i--) {
+ out.putInt(FRAMEWORK_FEATURES[i].length());
+ putString(out, FRAMEWORK_FEATURES[i]);
}
return new Chunk(CHUNK_FEAT, out);
diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java
new file mode 100644
index 0000000..a0578fb
--- /dev/null
+++ b/core/java/android/ddm/DdmHandleViewDebug.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2013 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 android.ddm;
+
+import android.opengl.GLUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewRootImpl;
+import android.view.WindowManagerGlobal;
+
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * Handle various requests related to profiling / debugging of the view system.
+ * Support for these features are advertised via {@link DdmHandleHello}.
+ */
+public class DdmHandleViewDebug extends ChunkHandler {
+ /** Enable/Disable tracing of OpenGL calls. */
+ public static final int CHUNK_VUGL = type("VUGL");
+
+ /** List {@link ViewRootImpl}'s of this process. */
+ private static final int CHUNK_VULW = type("VULW");
+
+ /** Operation on view root, first parameter in packet should be one of VURT_* constants */
+ private 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;
+
+ /**
+ * Generic View Operation, first parameter in the packet should be one of the
+ * VUOP_* constants below.
+ */
+ private 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;
+
+ /** Invalidate View. */
+ private static final int VUOP_INVALIDATE_VIEW = 3;
+
+ /** Re-layout given view. */
+ private static final int VUOP_LAYOUT_VIEW = 4;
+
+ /** Profile a view. */
+ private static final int VUOP_PROFILE_VIEW = 5;
+
+ /** Error code indicating operation specified in chunk is invalid. */
+ private static final int ERR_INVALID_OP = -1;
+
+ /** Error code indicating that the parameters are invalid. */
+ private static final int ERR_INVALID_PARAM = -2;
+
+ private static final DdmHandleViewDebug sInstance = new DdmHandleViewDebug();
+
+ /** singleton, do not instantiate. */
+ private DdmHandleViewDebug() {}
+
+ public static void register() {
+ DdmServer.registerHandler(CHUNK_VUGL, sInstance);
+ DdmServer.registerHandler(CHUNK_VULW, sInstance);
+ DdmServer.registerHandler(CHUNK_VURT, sInstance);
+ DdmServer.registerHandler(CHUNK_VUOP, sInstance);
+ }
+
+ @Override
+ public void connected() {
+ }
+
+ @Override
+ public void disconnected() {
+ }
+
+ @Override
+ public Chunk handleChunk(Chunk request) {
+ int type = request.type;
+
+ if (type == CHUNK_VUGL) {
+ return handleOpenGlTrace(request);
+ } else if (type == CHUNK_VULW) {
+ return listWindows();
+ }
+
+ ByteBuffer in = wrapChunk(request);
+ int op = in.getInt();
+
+ View rootView = getRootView(in);
+ if (rootView == null) {
+ return createFailChunk(ERR_INVALID_PARAM, "Invalid View Root");
+ }
+
+ if (type == CHUNK_VURT) {
+ if (op == VURT_DUMP_HIERARCHY)
+ return dumpHierarchy(rootView, in);
+ else if (op == VURT_CAPTURE_LAYERS)
+ return captureLayers(rootView);
+ else
+ return createFailChunk(ERR_INVALID_OP, "Unknown view root operation: " + op);
+ }
+
+ final View targetView = getTargetView(rootView, in);
+ if (targetView == null) {
+ return createFailChunk(ERR_INVALID_PARAM, "Invalid target view");
+ }
+
+ if (type == CHUNK_VUOP) {
+ switch (op) {
+ case VUOP_CAPTURE_VIEW:
+ return captureView(rootView, targetView);
+ case VUOP_DUMP_DISPLAYLIST:
+ return dumpDisplayLists(rootView, targetView);
+ case VUOP_INVALIDATE_VIEW:
+ return invalidateView(rootView, targetView);
+ case VUOP_LAYOUT_VIEW:
+ return layoutView(rootView, targetView);
+ case VUOP_PROFILE_VIEW:
+ return profileView(rootView, targetView);
+ default:
+ return createFailChunk(ERR_INVALID_OP, "Unknown view operation: " + op);
+ }
+ } else {
+ throw new RuntimeException("Unknown packet " + ChunkHandler.name(type));
+ }
+ }
+
+ private Chunk handleOpenGlTrace(Chunk request) {
+ ByteBuffer in = wrapChunk(request);
+ GLUtils.setTracingLevel(in.getInt());
+ return null; // empty response
+ }
+
+ /** Returns the list of windows owned by this client. */
+ private Chunk listWindows() {
+ String[] windowNames = WindowManagerGlobal.getInstance().getViewRootNames();
+
+ int responseLength = 4; // # of windows
+ for (String name : windowNames) {
+ responseLength += 4; // length of next window name
+ responseLength += name.length() * 2; // window name
+ }
+
+ ByteBuffer out = ByteBuffer.allocate(responseLength);
+ out.order(ChunkHandler.CHUNK_ORDER);
+
+ out.putInt(windowNames.length);
+ for (String name : windowNames) {
+ out.putInt(name.length());
+ putString(out, name);
+ }
+
+ return new Chunk(CHUNK_VULW, out);
+ }
+
+ private View getRootView(ByteBuffer in) {
+ try {
+ int viewRootNameLength = in.getInt();
+ String viewRootName = getString(in, viewRootNameLength);
+ return WindowManagerGlobal.getInstance().getRootView(viewRootName);
+ } catch (BufferUnderflowException e) {
+ return null;
+ }
+ }
+
+ private View getTargetView(View root, ByteBuffer in) {
+ int viewLength;
+ String viewName;
+
+ try {
+ viewLength = in.getInt();
+ viewName = getString(in, viewLength);
+ } catch (BufferUnderflowException e) {
+ return null;
+ }
+
+ return ViewDebug.findView(root, viewName);
+ }
+
+ /**
+ * Returns the view hierarchy and/or view properties starting at the provided view.
+ * Based on the input options, the return data may include:
+ * - just the view hierarchy
+ * - view hierarchy & the properties for each of the views
+ * - just the view properties for a specific view.
+ * TODO: Currently this only returns views starting at the root, need to fix so that
+ * it can return properties of any view.
+ */
+ private Chunk dumpHierarchy(View rootView, ByteBuffer in) {
+ boolean skipChildren = in.getInt() > 0;
+ boolean includeProperties = in.getInt() > 0;
+
+ ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
+ try {
+ ViewDebug.dump(rootView, skipChildren, includeProperties, b);
+ } catch (IOException e) {
+ return createFailChunk(1, "Unexpected error while obtaining view hierarchy: "
+ + e.getMessage());
+ }
+
+ byte[] data = b.toByteArray();
+ return new Chunk(CHUNK_VURT, data, 0, data.length);
+ }
+
+ /** Returns a buffer with region details & bitmap of every single view. */
+ private Chunk captureLayers(View rootView) {
+ ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
+ DataOutputStream dos = new DataOutputStream(b);
+ try {
+ ViewDebug.captureLayers(rootView, dos);
+ } catch (IOException e) {
+ return createFailChunk(1, "Unexpected error while obtaining view hierarchy: "
+ + e.getMessage());
+ } finally {
+ try {
+ dos.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+
+ byte[] data = b.toByteArray();
+ return new Chunk(CHUNK_VURT, data, 0, data.length);
+ }
+
+ private Chunk captureView(View rootView, View targetView) {
+ ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
+ try {
+ ViewDebug.capture(rootView, b, targetView);
+ } catch (IOException e) {
+ return createFailChunk(1, "Unexpected error while capturing view: "
+ + e.getMessage());
+ }
+
+ byte[] data = b.toByteArray();
+ return new Chunk(CHUNK_VUOP, data, 0, data.length);
+ }
+
+ /** Returns the display lists corresponding to the provided view. */
+ private Chunk dumpDisplayLists(final View rootView, final View targetView) {
+ rootView.post(new Runnable() {
+ @Override
+ public void run() {
+ ViewDebug.outputDisplayList(rootView, targetView);
+ }
+ });
+ return null;
+ }
+
+ /** Invalidates provided view. */
+ private Chunk invalidateView(final View rootView, final View targetView) {
+ targetView.postInvalidate();
+ return null;
+ }
+
+ /** Lays out provided view. */
+ private Chunk layoutView(View rootView, final View targetView) {
+ rootView.post(new Runnable() {
+ @Override
+ public void run() {
+ targetView.requestLayout();
+ }
+ });
+ return null;
+ }
+
+ /** Profiles provided view. */
+ private Chunk profileView(View rootView, final View targetView) {
+ ByteArrayOutputStream b = new ByteArrayOutputStream(32 * 1024);
+ BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(b), 32 * 1024);
+ try {
+ ViewDebug.profileViewAndChildren(targetView, bw);
+ } catch (IOException e) {
+ return createFailChunk(1, "Unexpected error while profiling view: " + e.getMessage());
+ } finally {
+ try {
+ bw.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+
+ byte[] data = b.toByteArray();
+ return new Chunk(CHUNK_VUOP, data, 0, data.length);
+ }
+}
diff --git a/core/java/android/ddm/DdmRegister.java b/core/java/android/ddm/DdmRegister.java
index 2c82967..e0faa51 100644
--- a/core/java/android/ddm/DdmRegister.java
+++ b/core/java/android/ddm/DdmRegister.java
@@ -51,7 +51,7 @@ public class DdmRegister {
DdmHandleNativeHeap.register();
DdmHandleProfiling.register();
DdmHandleExit.register();
- DdmHandleGlTracing.register();
+ DdmHandleViewDebug.register();
DdmServer.registrationComplete();
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 023e58f..6e28268 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -406,7 +406,7 @@ public class ViewDebug {
view = view.getRootView();
if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
- dump(view, clientStream);
+ dump(view, false, true, clientStream);
} else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) {
captureLayers(view, new DataOutputStream(clientStream));
} else {
@@ -425,7 +425,8 @@ public class ViewDebug {
}
}
- private static View findView(View root, String parameter) {
+ /** @hide */
+ public static View findView(View root, String parameter) {
// Look by type/hashcode
if (parameter.indexOf('@') != -1) {
final String[] ids = parameter.split("@");
@@ -488,7 +489,8 @@ public class ViewDebug {
}
}
- private static void profileViewAndChildren(final View view, BufferedWriter out)
+ /** @hide */
+ public static void profileViewAndChildren(final View view, BufferedWriter out)
throws IOException {
profileViewAndChildren(view, out, true);
}
@@ -623,7 +625,8 @@ public class ViewDebug {
return duration[0];
}
- private static void captureLayers(View root, final DataOutputStream clientStream)
+ /** @hide */
+ public static void captureLayers(View root, final DataOutputStream clientStream)
throws IOException {
try {
@@ -695,10 +698,21 @@ public class ViewDebug {
view.getViewRootImpl().outputDisplayList(view);
}
+ /** @hide */
+ public static void outputDisplayList(View root, View target) {
+ root.getViewRootImpl().outputDisplayList(target);
+ }
+
private static void capture(View root, final OutputStream clientStream, String parameter)
throws IOException {
final View captureView = findView(root, parameter);
+ capture(root, clientStream, captureView);
+ }
+
+ /** @hide */
+ public static void capture(View root, final OutputStream clientStream, View captureView)
+ throws IOException {
Bitmap b = performViewCapture(captureView, false);
if (b == null) {
@@ -752,14 +766,20 @@ public class ViewDebug {
return null;
}
- private static void dump(View root, OutputStream clientStream) throws IOException {
+ /**
+ * Dumps the view hierarchy starting from the given view.
+ * @hide
+ */
+ public static void dump(View root, boolean skipChildren, boolean includeProperties,
+ OutputStream clientStream) throws IOException {
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024);
View view = root.getRootView();
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
- dumpViewHierarchyWithProperties(group.getContext(), group, out, 0);
+ dumpViewHierarchy(group.getContext(), group, out, 0,
+ skipChildren, includeProperties);
}
out.write("DONE.");
out.newLine();
@@ -804,9 +824,13 @@ public class ViewDebug {
return view.getClass().getName().equals(className) && view.hashCode() == hashCode;
}
- private static void dumpViewHierarchyWithProperties(Context context, ViewGroup group,
- BufferedWriter out, int level) {
- if (!dumpViewWithProperties(context, group, out, level)) {
+ private static void dumpViewHierarchy(Context context, ViewGroup group,
+ BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
+ if (!dumpView(context, group, out, level, includeProperties)) {
+ return;
+ }
+
+ if (skipChildren) {
return;
}
@@ -814,9 +838,10 @@ public class ViewDebug {
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
if (view instanceof ViewGroup) {
- dumpViewHierarchyWithProperties(context, (ViewGroup) view, out, level + 1);
+ dumpViewHierarchy(context, (ViewGroup) view, out, level + 1, skipChildren,
+ includeProperties);
} else {
- dumpViewWithProperties(context, view, out, level + 1);
+ dumpView(context, view, out, level + 1, includeProperties);
}
}
if (group instanceof HierarchyHandler) {
@@ -824,8 +849,8 @@ public class ViewDebug {
}
}
- private static boolean dumpViewWithProperties(Context context, View view,
- BufferedWriter out, int level) {
+ private static boolean dumpView(Context context, View view,
+ BufferedWriter out, int level, boolean includeProperties) {
try {
for (int i = 0; i < level; i++) {
@@ -835,7 +860,9 @@ public class ViewDebug {
out.write('@');
out.write(Integer.toHexString(view.hashCode()));
out.write(' ');
- dumpViewProperties(context, view, out);
+ if (includeProperties) {
+ dumpViewProperties(context, view, out);
+ }
out.newLine();
} catch (IOException e) {
Log.w("View", "Error while dumping hierarchy tree");
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index e8945aa..7eb26fa 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -160,6 +160,29 @@ public final class WindowManagerGlobal {
}
}
+ public String[] getViewRootNames() {
+ synchronized (mLock) {
+ if (mRoots == null) return new String[0];
+ String[] mViewRoots = new String[mRoots.length];
+ int i = 0;
+ for (ViewRootImpl root : mRoots) {
+ mViewRoots[i++] = getWindowName(root);
+ }
+ return mViewRoots;
+ }
+ }
+
+ public View getRootView(String name) {
+ synchronized (mLock) {
+ if (mRoots == null) return null;
+ for (ViewRootImpl root : mRoots) {
+ if (name.equals(getWindowName(root))) return root.getView();
+ }
+ }
+
+ return null;
+ }
+
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {