summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/ddm/DdmHandleViewDebug.java143
-rw-r--r--core/java/android/view/ViewDebug.java65
2 files changed, 187 insertions, 21 deletions
diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java
index a0578fbcafbe..ce837966c12b 100644
--- a/core/java/android/ddm/DdmHandleViewDebug.java
+++ b/core/java/android/ddm/DdmHandleViewDebug.java
@@ -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;
@@ -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;
@@ -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. */
@@ -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);
}
@@ -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;
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 6e2826881b1d..987ff7857c32 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -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.
@@ -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);
+ }
+ });
+ }
}