Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c55420dc authored by Mike Schneider's avatar Mike Schneider Committed by Automerger Merge Worker
Browse files

Merge "Extend the INVOKE_VIEW_METHOD of the "Generic View Operation" (VUOP)...

Merge "Extend the INVOKE_VIEW_METHOD of the "Generic View Operation" (VUOP) DDM handler to 1) Return the invoked method's return value 2) In addition to the primitive values, support String and byte[]" into tm-qpr-dev am: da9ab025

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19898484



Change-Id: I35f5382940664b0dbba331f1f8868c7ddd13ae8a
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 20365ba6 da9ab025
Loading
Loading
Loading
Loading
+234 −63
Original line number Diff line number Diff line
@@ -16,12 +16,16 @@

package android.ddm;

import static com.android.internal.util.Preconditions.checkArgument;

import android.util.Log;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;

import com.android.internal.annotations.VisibleForTesting;

import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
@@ -34,6 +38,7 @@ import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

/**
 * Handle various requests related to profiling / debugging of the view system.
@@ -123,15 +128,16 @@ public class DdmHandleViewDebug extends DdmHandle {
        }

        if (type == CHUNK_VURT) {
            if (op == VURT_DUMP_HIERARCHY)
            if (op == VURT_DUMP_HIERARCHY) {
                return dumpHierarchy(rootView, in);
            else if (op == VURT_CAPTURE_LAYERS)
            } else if (op == VURT_CAPTURE_LAYERS) {
                return captureLayers(rootView);
            else if (op == VURT_DUMP_THEME)
            } else if (op == VURT_DUMP_THEME) {
                return dumpTheme(rootView);
            else
            } else {
                return createFailChunk(ERR_INVALID_OP, "Unknown view root operation: " + op);
            }
        }

        final View targetView = getTargetView(rootView, in);
        if (targetView == null) {
@@ -305,16 +311,46 @@ public class DdmHandleViewDebug extends DdmHandle {
     * 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> method name (encoded as UTF-16 2-byte characters) </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>
     * The type specifier is one character modelled after JNI signatures:
     *          <ul>
     *              <li>[ - array<br>
     *                This is followed by a second character according to this spec, indicating the
     *                array type, then the array length as an Int, followed by a repeated encoding
     *                of the actual data.
     *                WARNING: Only <b>byte[]</b> is supported currently.
     *              </li>
     *              <li>Z - boolean<br>
     *                 Booleans are encoded via bytes with 0 indicating false</li>
     *              <li>B - byte</li>
     *              <li>C - char</li>
     *              <li>S - short</li>
     *              <li>I - int</li>
     *              <li>J - long</li>
     *              <li>F - float</li>
     *              <li>D - double</li>
     *              <li>V - void<br>
     *                NOT followed by a value. Only used for return types</li>
     *              <li>R - String (not a real JNI type, but added for convenience)<br>
     *                Strings are encoded as an unsigned short of the number of <b>bytes</b>,
     *                followed by the actual UTF-8 encoded bytes.
     *                WARNING: This is the same encoding as produced by
     *                ViewHierarchyEncoder#writeString. However, note that this encoding is
     *                different to what DdmHandle#getString() expects, which is used in other places
     *                in this class.
     *                WARNING: Since the length is the number of UTF-8 encoded bytes, Strings can
     *                contain up to 64k ASCII characters, yet depending on the actual data, the true
     *                maximum might be as little as 21844 unicode characters.
     *                <b>null</b> String objects are encoded as an empty string
     *              </li>
     *            </ul>
     *   </li>
     * </ol>
     * Methods that take no arguments need only specify the method name.
     *
     * The return value is encoded the same way as a single parameter (type + value)
     */
    private Chunk invokeViewMethod(final View rootView, final View targetView, ByteBuffer in) {
        int l = in.getInt();
@@ -327,54 +363,17 @@ public class DdmHandleViewDebug extends DdmHandle {
            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.");
                }
            try {
                deserializeMethodParameters(args, argTypes, in);
            } catch (ViewMethodInvocationSerializationException e) {
                return createFailChunk(ERR_INVALID_PARAM, e.getMessage());
            }
        }

        Method method = null;
        Method method;
        try {
            method = targetView.getClass().getMethod(methodName, argTypes);
        } catch (NoSuchMethodException e) {
@@ -384,7 +383,10 @@ public class DdmHandleViewDebug extends DdmHandle {
        }

        try {
            ViewDebug.invokeViewMethod(targetView, method, args);
            Object result = ViewDebug.invokeViewMethod(targetView, method, args);
            Class<?> returnType = method.getReturnType();
            byte[] returnValue = serializeReturnValue(returnType, returnType.cast(result));
            return new Chunk(CHUNK_VUOP, returnValue, 0, returnValue.length);
        } catch (Exception e) {
            Log.e(TAG, "Exception while invoking method: " + e.getCause().getMessage());
            String msg = e.getCause().getMessage();
@@ -393,8 +395,6 @@ public class DdmHandleViewDebug extends DdmHandle {
            }
            return createFailChunk(ERR_EXCEPTION, msg);
        }

        return null;
    }

    private Chunk setLayoutParameter(final View rootView, final View targetView, ByteBuffer in) {
@@ -431,4 +431,175 @@ public class DdmHandleViewDebug extends DdmHandle {
        byte[] data = b.toByteArray();
        return new Chunk(CHUNK_VUOP, data, 0, data.length);
    }

    /**
     * Deserializes parameters according to the VUOP_INVOKE_VIEW_METHOD protocol the {@code in}
     * buffer.
     *
     * The length of {@code args} determines how many arguments are read. The {@code argTypes} must
     * be the same length, and will be set to the argument types of the data read.
     *
     * @hide
     */
    @VisibleForTesting
    public static void deserializeMethodParameters(
            Object[] args, Class<?>[] argTypes, ByteBuffer in) throws
            ViewMethodInvocationSerializationException {
        checkArgument(args.length == argTypes.length);

        for (int i = 0; i < args.length; i++) {
            char typeSignature = in.getChar();
            boolean isArray = typeSignature == SIG_ARRAY;
            if (isArray) {
                char arrayType = in.getChar();
                if (arrayType != SIG_BYTE) {
                    // This implementation only supports byte-arrays for now.
                    throw new ViewMethodInvocationSerializationException(
                            "Unsupported array parameter type (" + typeSignature
                                    + ") to invoke view method @argument " + i);
                }

                int arrayLength = in.getInt();
                if (arrayLength > in.remaining()) {
                    // The sender did not actually sent the specified amount of bytes. This
                    // avoids a malformed packet to trigger an out-of-memory error.
                    throw new BufferUnderflowException();
                }

                byte[] byteArray = new byte[arrayLength];
                in.get(byteArray);

                argTypes[i] = byte[].class;
                args[i] = byteArray;
            } else {
                switch (typeSignature) {
                    case SIG_BOOLEAN:
                        argTypes[i] = boolean.class;
                        args[i] = in.get() != 0;
                        break;
                    case SIG_BYTE:
                        argTypes[i] = byte.class;
                        args[i] = in.get();
                        break;
                    case SIG_CHAR:
                        argTypes[i] = char.class;
                        args[i] = in.getChar();
                        break;
                    case SIG_SHORT:
                        argTypes[i] = short.class;
                        args[i] = in.getShort();
                        break;
                    case SIG_INT:
                        argTypes[i] = int.class;
                        args[i] = in.getInt();
                        break;
                    case SIG_LONG:
                        argTypes[i] = long.class;
                        args[i] = in.getLong();
                        break;
                    case SIG_FLOAT:
                        argTypes[i] = float.class;
                        args[i] = in.getFloat();
                        break;
                    case SIG_DOUBLE:
                        argTypes[i] = double.class;
                        args[i] = in.getDouble();
                        break;
                    case SIG_STRING: {
                        argTypes[i] = String.class;
                        int stringUtf8ByteCount = Short.toUnsignedInt(in.getShort());
                        byte[] rawStringBuffer = new byte[stringUtf8ByteCount];
                        in.get(rawStringBuffer);
                        args[i] = new String(rawStringBuffer, StandardCharsets.UTF_8);
                        break;
                    }
                    default:
                        Log.e(TAG, "arg " + i + ", unrecognized type: " + typeSignature);
                        throw new ViewMethodInvocationSerializationException(
                                "Unsupported parameter type (" + typeSignature
                                        + ") to invoke view method.");
                }
            }

        }
    }

    /**
     * Serializes {@code value} to the wire protocol of VUOP_INVOKE_VIEW_METHOD.
     * @hide
     */
    @VisibleForTesting
    public static byte[] serializeReturnValue(Class<?> type, Object value)
            throws ViewMethodInvocationSerializationException, IOException {
        ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream(1024);
        DataOutputStream dos = new DataOutputStream(byteOutStream);

        if (type.isArray()) {
            if (!type.equals(byte[].class)) {
                // Only byte arrays are supported currently.
                throw new ViewMethodInvocationSerializationException(
                        "Unsupported array return type (" + type + ")");
            }
            byte[] byteArray = (byte[]) value;
            dos.writeChar(SIG_ARRAY);
            dos.writeChar(SIG_BYTE);
            dos.writeInt(byteArray.length);
            dos.write(byteArray);
        } else if (boolean.class.equals(type)) {
            dos.writeChar(SIG_BOOLEAN);
            dos.write((boolean) value ? 1 : 0);
        } else if (byte.class.equals(type)) {
            dos.writeChar(SIG_BYTE);
            dos.writeByte((byte) value);
        } else if (char.class.equals(type)) {
            dos.writeChar(SIG_CHAR);
            dos.writeChar((char) value);
        } else if (short.class.equals(type)) {
            dos.writeChar(SIG_SHORT);
            dos.writeShort((short) value);
        } else if (int.class.equals(type)) {
            dos.writeChar(SIG_INT);
            dos.writeInt((int) value);
        } else if (long.class.equals(type)) {
            dos.writeChar(SIG_LONG);
            dos.writeLong((long) value);
        } else if (double.class.equals(type)) {
            dos.writeChar(SIG_DOUBLE);
            dos.writeDouble((double) value);
        } else if (float.class.equals(type)) {
            dos.writeChar(SIG_FLOAT);
            dos.writeFloat((float) value);
        } else if (String.class.equals(type)) {
            dos.writeChar(SIG_STRING);
            dos.writeUTF(value != null ? (String) value : "");
        } else {
            dos.writeChar(SIG_VOID);
        }

        return byteOutStream.toByteArray();
    }

    // Prefixes for simple primitives. These match the JNI definitions.
    private static final char SIG_ARRAY = '[';
    private static final char SIG_BOOLEAN = 'Z';
    private static final char SIG_BYTE = 'B';
    private static final char SIG_SHORT = 'S';
    private static final char SIG_CHAR = 'C';
    private static final char SIG_INT = 'I';
    private static final char SIG_LONG = 'J';
    private static final char SIG_FLOAT = 'F';
    private static final char SIG_DOUBLE = 'D';
    private static final char SIG_VOID = 'V';
    // Prefixes for some commonly used objects
    private static final char SIG_STRING = 'R';

    /**
     * @hide
     */
    @VisibleForTesting
    public static class ViewMethodInvocationSerializationException extends Exception {
        ViewMethodInvocationSerializationException(String message) {
            super(message);
        }
    }
}
+398 −0

File added.

Preview size limit exceeded, changes collapsed.