Loading core/java/android/ddm/DdmHandleViewDebug.java +122 −21 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.reflect.Method; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; Loading Loading @@ -67,14 +68,14 @@ public class DdmHandleViewDebug extends ChunkHandler { /** 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; /** Profile a view. */ private static final int VUOP_PROFILE_VIEW = 3; /** Re-layout given view. */ private static final int VUOP_LAYOUT_VIEW = 4; /** Invoke a method on the view. */ private static final int VUOP_INVOKE_VIEW_METHOD = 4; /** Profile a view. */ private static final int VUOP_PROFILE_VIEW = 5; /** Set layout parameter. */ private static final int VUOP_SET_LAYOUT_PARAMETER = 5; /** Error code indicating operation specified in chunk is invalid. */ private static final int ERR_INVALID_OP = -1; Loading @@ -82,6 +83,11 @@ public class DdmHandleViewDebug extends ChunkHandler { /** Error code indicating that the parameters are invalid. */ private static final int ERR_INVALID_PARAM = -2; /** Error code indicating an exception while performing operation. */ private static final int ERR_EXCEPTION = -3; private static final String TAG = "DdmViewDebug"; private static final DdmHandleViewDebug sInstance = new DdmHandleViewDebug(); /** singleton, do not instantiate. */ Loading Loading @@ -140,12 +146,12 @@ public class DdmHandleViewDebug extends ChunkHandler { 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); case VUOP_INVOKE_VIEW_METHOD: return invokeViewMethod(rootView, targetView, in); case VUOP_SET_LAYOUT_PARAMETER: return setLayoutParameter(rootView, targetView, in); default: return createFailChunk(ERR_INVALID_OP, "Unknown view operation: " + op); } Loading Loading @@ -276,20 +282,115 @@ public class DdmHandleViewDebug extends ChunkHandler { return null; } /** Invalidates provided view. */ private Chunk invalidateView(final View rootView, final View targetView) { targetView.postInvalidate(); /** * Invokes provided method on the view. * The method name and its arguments are passed in as inputs via the byte buffer. * The buffer contains:<ol> * <li> len(method name) </li> * <li> method name </li> * <li> # of args </li> * <li> arguments: Each argument comprises of a type specifier followed by the actual argument. * The type specifier is a single character as used in JNI: * (Z - boolean, B - byte, C - char, S - short, I - int, J - long, * F - float, D - double). <p> * The type specifier is followed by the actual value of argument. * Booleans are encoded via bytes with 0 indicating false.</li> * </ol> * Methods that take no arguments need only specify the method name. */ private Chunk invokeViewMethod(final View rootView, final View targetView, ByteBuffer in) { int l = in.getInt(); String methodName = getString(in, l); Class<?>[] argTypes; Object[] args; if (!in.hasRemaining()) { argTypes = new Class<?>[0]; args = new Object[0]; } else { int nArgs = in.getInt(); argTypes = new Class<?>[nArgs]; args = new Object[nArgs]; for (int i = 0; i < nArgs; i++) { char c = in.getChar(); switch (c) { case 'Z': argTypes[i] = boolean.class; args[i] = in.get() == 0 ? false : true; break; case 'B': argTypes[i] = byte.class; args[i] = in.get(); break; case 'C': argTypes[i] = char.class; args[i] = in.getChar(); break; case 'S': argTypes[i] = short.class; args[i] = in.getShort(); break; case 'I': argTypes[i] = int.class; args[i] = in.getInt(); break; case 'J': argTypes[i] = long.class; args[i] = in.getLong(); break; case 'F': argTypes[i] = float.class; args[i] = in.getFloat(); break; case 'D': argTypes[i] = double.class; args[i] = in.getDouble(); break; default: Log.e(TAG, "arg " + i + ", unrecognized type: " + c); return createFailChunk(ERR_INVALID_PARAM, "Unsupported parameter type (" + c + ") to invoke view method."); } } } Method method = null; try { method = targetView.getClass().getMethod(methodName, argTypes); } catch (NoSuchMethodException e) { Log.e(TAG, "No such method: " + e.getMessage()); return createFailChunk(ERR_INVALID_PARAM, "No such method: " + e.getMessage()); } try { ViewDebug.invokeViewMethod(targetView, method, args); } catch (Exception e) { Log.e(TAG, "Exception while invoking method: " + e.getCause().getMessage()); String msg = e.getCause().getMessage(); if (msg == null) { msg = e.getCause().toString(); } return createFailChunk(ERR_EXCEPTION, msg); } return null; } /** Lays out provided view. */ private Chunk layoutView(View rootView, final View targetView) { rootView.post(new Runnable() { @Override public void run() { targetView.requestLayout(); private Chunk setLayoutParameter(final View rootView, final View targetView, ByteBuffer in) { int l = in.getInt(); String param = getString(in, l); int value = in.getInt(); try { ViewDebug.setLayoutParameter(targetView, param, value); } catch (Exception e) { Log.e(TAG, "Exception setting layout parameter: " + e); return createFailChunk(ERR_EXCEPTION, "Error accessing field " + param + ":" + e.getMessage()); } }); return null; } Loading core/java/android/view/ViewDebug.java +65 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** * Various debugging/tracing tools related to {@link View} and the view hierarchy. Loading Loading @@ -1374,4 +1375,68 @@ public class ViewDebug { sb.append(capturedViewExportMethods(view, klass, "")); Log.d(tag, sb.toString()); } /** * Invoke a particular method on given view. * The given method is always invoked on the UI thread. The caller thread will stall until the * method invocation is complete. Returns an object equal to the result of the method * invocation, null if the method is declared to return void * @throws Exception if the method invocation caused any exception * @hide */ public static Object invokeViewMethod(final View view, final Method method, final Object[] args) { final CountDownLatch latch = new CountDownLatch(1); final AtomicReference<Object> result = new AtomicReference<Object>(); final AtomicReference<Throwable> exception = new AtomicReference<Throwable>(); view.post(new Runnable() { @Override public void run() { try { result.set(method.invoke(view, args)); } catch (InvocationTargetException e) { exception.set(e.getCause()); } catch (Exception e) { exception.set(e); } latch.countDown(); } }); try { latch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } if (exception.get() != null) { throw new RuntimeException(exception.get()); } return result.get(); } /** * @hide */ public static void setLayoutParameter(final View view, final String param, final int value) throws NoSuchFieldException, IllegalAccessException { final ViewGroup.LayoutParams p = view.getLayoutParams(); final Field f = p.getClass().getField(param); if (f.getType() != int.class) { throw new RuntimeException("Only integer layout parameters can be set. Field " + param + " is of type " + f.getType().getSimpleName()); } f.set(p, Integer.valueOf(value)); view.post(new Runnable() { @Override public void run() { view.setLayoutParams(p); } }); } } Loading
core/java/android/ddm/DdmHandleViewDebug.java +122 −21 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.reflect.Method; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; Loading Loading @@ -67,14 +68,14 @@ public class DdmHandleViewDebug extends ChunkHandler { /** 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; /** Profile a view. */ private static final int VUOP_PROFILE_VIEW = 3; /** Re-layout given view. */ private static final int VUOP_LAYOUT_VIEW = 4; /** Invoke a method on the view. */ private static final int VUOP_INVOKE_VIEW_METHOD = 4; /** Profile a view. */ private static final int VUOP_PROFILE_VIEW = 5; /** Set layout parameter. */ private static final int VUOP_SET_LAYOUT_PARAMETER = 5; /** Error code indicating operation specified in chunk is invalid. */ private static final int ERR_INVALID_OP = -1; Loading @@ -82,6 +83,11 @@ public class DdmHandleViewDebug extends ChunkHandler { /** Error code indicating that the parameters are invalid. */ private static final int ERR_INVALID_PARAM = -2; /** Error code indicating an exception while performing operation. */ private static final int ERR_EXCEPTION = -3; private static final String TAG = "DdmViewDebug"; private static final DdmHandleViewDebug sInstance = new DdmHandleViewDebug(); /** singleton, do not instantiate. */ Loading Loading @@ -140,12 +146,12 @@ public class DdmHandleViewDebug extends ChunkHandler { 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); case VUOP_INVOKE_VIEW_METHOD: return invokeViewMethod(rootView, targetView, in); case VUOP_SET_LAYOUT_PARAMETER: return setLayoutParameter(rootView, targetView, in); default: return createFailChunk(ERR_INVALID_OP, "Unknown view operation: " + op); } Loading Loading @@ -276,20 +282,115 @@ public class DdmHandleViewDebug extends ChunkHandler { return null; } /** Invalidates provided view. */ private Chunk invalidateView(final View rootView, final View targetView) { targetView.postInvalidate(); /** * Invokes provided method on the view. * The method name and its arguments are passed in as inputs via the byte buffer. * The buffer contains:<ol> * <li> len(method name) </li> * <li> method name </li> * <li> # of args </li> * <li> arguments: Each argument comprises of a type specifier followed by the actual argument. * The type specifier is a single character as used in JNI: * (Z - boolean, B - byte, C - char, S - short, I - int, J - long, * F - float, D - double). <p> * The type specifier is followed by the actual value of argument. * Booleans are encoded via bytes with 0 indicating false.</li> * </ol> * Methods that take no arguments need only specify the method name. */ private Chunk invokeViewMethod(final View rootView, final View targetView, ByteBuffer in) { int l = in.getInt(); String methodName = getString(in, l); Class<?>[] argTypes; Object[] args; if (!in.hasRemaining()) { argTypes = new Class<?>[0]; args = new Object[0]; } else { int nArgs = in.getInt(); argTypes = new Class<?>[nArgs]; args = new Object[nArgs]; for (int i = 0; i < nArgs; i++) { char c = in.getChar(); switch (c) { case 'Z': argTypes[i] = boolean.class; args[i] = in.get() == 0 ? false : true; break; case 'B': argTypes[i] = byte.class; args[i] = in.get(); break; case 'C': argTypes[i] = char.class; args[i] = in.getChar(); break; case 'S': argTypes[i] = short.class; args[i] = in.getShort(); break; case 'I': argTypes[i] = int.class; args[i] = in.getInt(); break; case 'J': argTypes[i] = long.class; args[i] = in.getLong(); break; case 'F': argTypes[i] = float.class; args[i] = in.getFloat(); break; case 'D': argTypes[i] = double.class; args[i] = in.getDouble(); break; default: Log.e(TAG, "arg " + i + ", unrecognized type: " + c); return createFailChunk(ERR_INVALID_PARAM, "Unsupported parameter type (" + c + ") to invoke view method."); } } } Method method = null; try { method = targetView.getClass().getMethod(methodName, argTypes); } catch (NoSuchMethodException e) { Log.e(TAG, "No such method: " + e.getMessage()); return createFailChunk(ERR_INVALID_PARAM, "No such method: " + e.getMessage()); } try { ViewDebug.invokeViewMethod(targetView, method, args); } catch (Exception e) { Log.e(TAG, "Exception while invoking method: " + e.getCause().getMessage()); String msg = e.getCause().getMessage(); if (msg == null) { msg = e.getCause().toString(); } return createFailChunk(ERR_EXCEPTION, msg); } return null; } /** Lays out provided view. */ private Chunk layoutView(View rootView, final View targetView) { rootView.post(new Runnable() { @Override public void run() { targetView.requestLayout(); private Chunk setLayoutParameter(final View rootView, final View targetView, ByteBuffer in) { int l = in.getInt(); String param = getString(in, l); int value = in.getInt(); try { ViewDebug.setLayoutParameter(targetView, param, value); } catch (Exception e) { Log.e(TAG, "Exception setting layout parameter: " + e); return createFailChunk(ERR_EXCEPTION, "Error accessing field " + param + ":" + e.getMessage()); } }); return null; } Loading
core/java/android/view/ViewDebug.java +65 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** * Various debugging/tracing tools related to {@link View} and the view hierarchy. Loading Loading @@ -1374,4 +1375,68 @@ public class ViewDebug { sb.append(capturedViewExportMethods(view, klass, "")); Log.d(tag, sb.toString()); } /** * Invoke a particular method on given view. * The given method is always invoked on the UI thread. The caller thread will stall until the * method invocation is complete. Returns an object equal to the result of the method * invocation, null if the method is declared to return void * @throws Exception if the method invocation caused any exception * @hide */ public static Object invokeViewMethod(final View view, final Method method, final Object[] args) { final CountDownLatch latch = new CountDownLatch(1); final AtomicReference<Object> result = new AtomicReference<Object>(); final AtomicReference<Throwable> exception = new AtomicReference<Throwable>(); view.post(new Runnable() { @Override public void run() { try { result.set(method.invoke(view, args)); } catch (InvocationTargetException e) { exception.set(e.getCause()); } catch (Exception e) { exception.set(e); } latch.countDown(); } }); try { latch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } if (exception.get() != null) { throw new RuntimeException(exception.get()); } return result.get(); } /** * @hide */ public static void setLayoutParameter(final View view, final String param, final int value) throws NoSuchFieldException, IllegalAccessException { final ViewGroup.LayoutParams p = view.getLayoutParams(); final Field f = p.getClass().getField(param); if (f.getType() != int.class) { throw new RuntimeException("Only integer layout parameters can be set. Field " + param + " is of type " + f.getType().getSimpleName()); } f.set(p, Integer.valueOf(value)); view.post(new Runnable() { @Override public void run() { view.setLayoutParams(p); } }); } }