Add SDK 29 sources.

Test: N/A
Change-Id: Iedb7a31029e003928eb16f7e69ed147e72bb6235
diff --git a/android/ddm/DdmHandleAppName.java b/android/ddm/DdmHandleAppName.java
new file mode 100644
index 0000000..9560787
--- /dev/null
+++ b/android/ddm/DdmHandleAppName.java
@@ -0,0 +1,110 @@
+/*
+ * 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 android.ddm;
+
+import android.annotation.UnsupportedAppUsage;
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+import android.util.Log;
+import java.nio.ByteBuffer;
+
+
+/**
+ * Track our app name.  We don't (currently) handle any inbound packets.
+ */
+public class DdmHandleAppName extends ChunkHandler {
+
+    public static final int CHUNK_APNM = type("APNM");
+
+    private volatile static String mAppName = "";
+
+    private static DdmHandleAppName mInstance = new DdmHandleAppName();
+
+
+    /* singleton, do not instantiate */
+    private DdmHandleAppName() {}
+
+    /**
+     * Register for the messages we're interested in.
+     */
+    public static void register() {}
+
+    /**
+     * Called when the DDM server connects.  The handler is allowed to
+     * send messages to the server.
+     */
+    public void connected() {}
+
+    /**
+     * Called when the DDM server disconnects.  Can be used to disable
+     * periodic transmissions or clean up saved state.
+     */
+    public void disconnected() {}
+
+    /**
+     * Handle a chunk of data.
+     */
+    public Chunk handleChunk(Chunk request) {
+        return null;
+    }
+
+
+
+    /**
+     * Set the application name.  Called when we get named, which may be
+     * before or after DDMS connects.  For the latter we need to send up
+     * an APNM message.
+     */
+    @UnsupportedAppUsage
+    public static void setAppName(String name, int userId) {
+        if (name == null || name.length() == 0)
+            return;
+
+        mAppName = name;
+
+        // if DDMS is already connected, send the app name up
+        sendAPNM(name, userId);
+    }
+
+    @UnsupportedAppUsage
+    public static String getAppName() {
+        return mAppName;
+    }
+
+    /*
+     * Send an APNM (APplication NaMe) chunk.
+     */
+    private static void sendAPNM(String appName, int userId) {
+        if (false)
+            Log.v("ddm", "Sending app name");
+
+        ByteBuffer out = ByteBuffer.allocate(
+                            4 /* appName's length */
+                            + appName.length()*2 /* appName */
+                            + 4 /* userId */);
+        out.order(ChunkHandler.CHUNK_ORDER);
+        out.putInt(appName.length());
+        putString(out, appName);
+        out.putInt(userId);
+
+        Chunk chunk = new Chunk(CHUNK_APNM, out);
+        DdmServer.sendChunk(chunk);
+    }
+
+}
+
diff --git a/android/ddm/DdmHandleExit.java b/android/ddm/DdmHandleExit.java
new file mode 100644
index 0000000..74ae37a
--- /dev/null
+++ b/android/ddm/DdmHandleExit.java
@@ -0,0 +1,77 @@
+/*
+ * 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 android.ddm;
+
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+import android.util.Log;
+import java.nio.ByteBuffer;
+
+/**
+ * Handle an EXIT chunk.
+ */
+public class DdmHandleExit extends ChunkHandler {
+
+    public static final int CHUNK_EXIT = type("EXIT");
+
+    private static DdmHandleExit mInstance = new DdmHandleExit();
+
+
+    /* singleton, do not instantiate */
+    private DdmHandleExit() {}
+
+    /**
+     * Register for the messages we're interested in.
+     */
+    public static void register() {
+        DdmServer.registerHandler(CHUNK_EXIT, mInstance);
+    }
+
+    /**
+     * Called when the DDM server connects.  The handler is allowed to
+     * send messages to the server.
+     */
+    public void connected() {}
+
+    /**
+     * Called when the DDM server disconnects.  Can be used to disable
+     * periodic transmissions or clean up saved state.
+     */
+    public void disconnected() {}
+
+    /**
+     * Handle a chunk of data.  We're only registered for "EXIT".
+     */
+    public Chunk handleChunk(Chunk request) {
+        if (false)
+            Log.v("ddm-exit", "Handling " + name(request.type) + " chunk");
+
+        /*
+         * Process the request.
+         */
+        ByteBuffer in = wrapChunk(request);
+
+        int statusCode = in.getInt();
+
+        Runtime.getRuntime().halt(statusCode);
+
+        // if that doesn't work, return an empty message
+        return null;
+    }
+}
+
diff --git a/android/ddm/DdmHandleHeap.java b/android/ddm/DdmHandleHeap.java
new file mode 100644
index 0000000..e24aeb2
--- /dev/null
+++ b/android/ddm/DdmHandleHeap.java
@@ -0,0 +1,269 @@
+/*
+ * 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 android.ddm;
+
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
+import android.os.Debug;
+import android.util.Log;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Handle native and virtual heap requests.
+ */
+public class DdmHandleHeap extends ChunkHandler {
+
+    public static final int CHUNK_HPIF = type("HPIF");
+    public static final int CHUNK_HPSG = type("HPSG");
+    public static final int CHUNK_HPDU = type("HPDU");
+    public static final int CHUNK_HPDS = type("HPDS");
+    public static final int CHUNK_NHSG = type("NHSG");
+    public static final int CHUNK_HPGC = type("HPGC");
+    public static final int CHUNK_REAE = type("REAE");
+    public static final int CHUNK_REAQ = type("REAQ");
+    public static final int CHUNK_REAL = type("REAL");
+
+    private static DdmHandleHeap mInstance = new DdmHandleHeap();
+
+
+    /* singleton, do not instantiate */
+    private DdmHandleHeap() {}
+
+    /**
+     * Register for the messages we're interested in.
+     */
+    public static void register() {
+        DdmServer.registerHandler(CHUNK_HPIF, mInstance);
+        DdmServer.registerHandler(CHUNK_HPSG, mInstance);
+        DdmServer.registerHandler(CHUNK_HPDU, mInstance);
+        DdmServer.registerHandler(CHUNK_HPDS, mInstance);
+        DdmServer.registerHandler(CHUNK_NHSG, mInstance);
+        DdmServer.registerHandler(CHUNK_HPGC, mInstance);
+        DdmServer.registerHandler(CHUNK_REAE, mInstance);
+        DdmServer.registerHandler(CHUNK_REAQ, mInstance);
+        DdmServer.registerHandler(CHUNK_REAL, mInstance);
+    }
+
+    /**
+     * Called when the DDM server connects.  The handler is allowed to
+     * send messages to the server.
+     */
+    public void connected() {}
+
+    /**
+     * Called when the DDM server disconnects.  Can be used to disable
+     * periodic transmissions or clean up saved state.
+     */
+    public void disconnected() {}
+
+    /**
+     * Handle a chunk of data.
+     */
+    public Chunk handleChunk(Chunk request) {
+        if (false)
+            Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
+        int type = request.type;
+
+        if (type == CHUNK_HPIF) {
+            return handleHPIF(request);
+        } else if (type == CHUNK_HPSG) {
+            return handleHPSGNHSG(request, false);
+        } else if (type == CHUNK_HPDU) {
+            return handleHPDU(request);
+        } else if (type == CHUNK_HPDS) {
+            return handleHPDS(request);
+        } else if (type == CHUNK_NHSG) {
+            return handleHPSGNHSG(request, true);
+        } else if (type == CHUNK_HPGC) {
+            return handleHPGC(request);
+        } else if (type == CHUNK_REAE) {
+            return handleREAE(request);
+        } else if (type == CHUNK_REAQ) {
+            return handleREAQ(request);
+        } else if (type == CHUNK_REAL) {
+            return handleREAL(request);
+        } else {
+            throw new RuntimeException("Unknown packet "
+                + ChunkHandler.name(type));
+        }
+    }
+
+    /*
+     * Handle a "HeaP InFo" request.
+     */
+    private Chunk handleHPIF(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+
+        int when = in.get();
+        if (false)
+            Log.v("ddm-heap", "Heap segment enable: when=" + when);
+
+        boolean ok = DdmVmInternal.heapInfoNotify(when);
+        if (!ok) {
+            return createFailChunk(1, "Unsupported HPIF what");
+        } else {
+            return null;        // empty response
+        }
+    }
+
+    /*
+     * Handle a "HeaP SeGment" or "Native Heap SeGment" request.
+     */
+    private Chunk handleHPSGNHSG(Chunk request, boolean isNative) {
+        ByteBuffer in = wrapChunk(request);
+
+        int when = in.get();
+        int what = in.get();
+        if (false)
+            Log.v("ddm-heap", "Heap segment enable: when=" + when
+                + ", what=" + what + ", isNative=" + isNative);
+
+        boolean ok = DdmVmInternal.heapSegmentNotify(when, what, isNative);
+        if (!ok) {
+            return createFailChunk(1, "Unsupported HPSG what/when");
+        } else {
+            // TODO: if "when" is non-zero and we want to see a dump
+            //       right away, initiate a GC.
+            return null;        // empty response
+        }
+    }
+
+    /*
+     * Handle a "HeaP DUmp" request.
+     *
+     * This currently just returns a result code.  We could pull up
+     * the entire contents of the file and return them, but hprof dump
+     * files can be a few megabytes.
+     */
+    private Chunk handleHPDU(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+        byte result;
+
+        /* get the filename for the output file */
+        int len = in.getInt();
+        String fileName = getString(in, len);
+        if (false)
+            Log.d("ddm-heap", "Heap dump: file='" + fileName + "'");
+
+        try {
+            Debug.dumpHprofData(fileName);
+            result = 0;
+        } catch (UnsupportedOperationException uoe) {
+            Log.w("ddm-heap", "hprof dumps not supported in this VM");
+            result = -1;
+        } catch (IOException ioe) {
+            result = -1;
+        } catch (RuntimeException re) {
+            result = -1;
+        }
+
+        /* create a non-empty reply so the handler fires on completion */
+        byte[] reply = { result };
+        return new Chunk(CHUNK_HPDU, reply, 0, reply.length);
+    }
+
+    /*
+     * Handle a "HeaP Dump Streaming" request.
+     *
+     * This tells the VM to create a heap dump and send it directly to
+     * DDMS.  The dumps are large enough that we don't want to copy the
+     * data into a byte[] and send it from here.
+     */
+    private Chunk handleHPDS(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+        byte result;
+
+        /* get the filename for the output file */
+        if (false)
+            Log.d("ddm-heap", "Heap dump: [DDMS]");
+
+        String failMsg = null;
+        try {
+            Debug.dumpHprofDataDdms();
+        } catch (UnsupportedOperationException uoe) {
+            failMsg = "hprof dumps not supported in this VM";
+        } catch (RuntimeException re) {
+            failMsg = "Exception: " + re.getMessage();
+        }
+
+        if (failMsg != null) {
+            Log.w("ddm-heap", failMsg);
+            return createFailChunk(1, failMsg);
+        } else {
+            return null;
+        }
+    }
+
+    /*
+     * Handle a "HeaP Garbage Collection" request.
+     */
+    private Chunk handleHPGC(Chunk request) {
+        //ByteBuffer in = wrapChunk(request);
+
+        if (false)
+            Log.d("ddm-heap", "Heap GC request");
+        Runtime.getRuntime().gc();
+
+        return null;        // empty response
+    }
+
+    /*
+     * Handle a "REcent Allocation Enable" request.
+     */
+    private Chunk handleREAE(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+        boolean enable;
+
+        enable = (in.get() != 0);
+
+        if (false)
+            Log.d("ddm-heap", "Recent allocation enable request: " + enable);
+
+        DdmVmInternal.enableRecentAllocations(enable);
+
+        return null;        // empty response
+    }
+
+    /*
+     * Handle a "REcent Allocation Query" request.
+     */
+    private Chunk handleREAQ(Chunk request) {
+        //ByteBuffer in = wrapChunk(request);
+
+        byte[] reply = new byte[1];
+        reply[0] = DdmVmInternal.getRecentAllocationStatus() ? (byte)1 :(byte)0;
+        return new Chunk(CHUNK_REAQ, reply, 0, reply.length);
+    }
+
+    /*
+     * Handle a "REcent ALlocations" request.
+     */
+    private Chunk handleREAL(Chunk request) {
+        //ByteBuffer in = wrapChunk(request);
+
+        if (false)
+            Log.d("ddm-heap", "Recent allocations request");
+
+        /* generate the reply in a ready-to-go format */
+        byte[] reply = DdmVmInternal.getRecentAllocations();
+        return new Chunk(CHUNK_REAL, reply, 0, reply.length);
+    }
+}
+
diff --git a/android/ddm/DdmHandleHello.java b/android/ddm/DdmHandleHello.java
new file mode 100644
index 0000000..87568e8
--- /dev/null
+++ b/android/ddm/DdmHandleHello.java
@@ -0,0 +1,219 @@
+/*
+ * 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 android.ddm;
+
+import android.os.Debug;
+import android.os.UserHandle;
+import android.util.Log;
+
+import dalvik.system.VMRuntime;
+
+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;
+
+/**
+ * Handle "hello" messages and feature discovery.
+ */
+public class DdmHandleHello extends ChunkHandler {
+
+    public static final int CHUNK_HELO = type("HELO");
+    public static final int CHUNK_WAIT = type("WAIT");
+    public static final int CHUNK_FEAT = type("FEAT");
+
+    private static final int CLIENT_PROTOCOL_VERSION = 1;
+
+    private static DdmHandleHello mInstance = new DdmHandleHello();
+
+    private static final String[] FRAMEWORK_FEATURES = new String[] {
+        "opengl-tracing",
+        "view-hierarchy",
+    };
+
+    /* singleton, do not instantiate */
+    private DdmHandleHello() {}
+
+    /**
+     * Register for the messages we're interested in.
+     */
+    public static void register() {
+        DdmServer.registerHandler(CHUNK_HELO, mInstance);
+        DdmServer.registerHandler(CHUNK_FEAT, mInstance);
+    }
+
+    /**
+     * Called when the DDM server connects.  The handler is allowed to
+     * send messages to the server.
+     */
+    public void connected() {
+        if (false)
+            Log.v("ddm-hello", "Connected!");
+
+        if (false) {
+            /* test spontaneous transmission */
+            byte[] data = new byte[] { 0, 1, 2, 3, 4, -4, -3, -2, -1, 127 };
+            Chunk testChunk =
+                new Chunk(ChunkHandler.type("TEST"), data, 1, data.length-2);
+            DdmServer.sendChunk(testChunk);
+        }
+    }
+
+    /**
+     * Called when the DDM server disconnects.  Can be used to disable
+     * periodic transmissions or clean up saved state.
+     */
+    public void disconnected() {
+        if (false)
+            Log.v("ddm-hello", "Disconnected!");
+    }
+
+    /**
+     * Handle a chunk of data.
+     */
+    public Chunk handleChunk(Chunk request) {
+        if (false)
+            Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
+        int type = request.type;
+
+        if (type == CHUNK_HELO) {
+            return handleHELO(request);
+        } else if (type == CHUNK_FEAT) {
+            return handleFEAT(request);
+        } else {
+            throw new RuntimeException("Unknown packet "
+                + ChunkHandler.name(type));
+        }
+    }
+
+    /*
+     * Handle introductory packet. This is called during JNI_CreateJavaVM
+     * before frameworks native methods are registered, so be careful not
+     * to call any APIs that depend on frameworks native code.
+     */
+    private Chunk handleHELO(Chunk request) {
+        if (false)
+            return createFailChunk(123, "This is a test");
+
+        /*
+         * Process the request.
+         */
+        ByteBuffer in = wrapChunk(request);
+
+        int serverProtoVers = in.getInt();
+        if (false)
+            Log.v("ddm-hello", "Server version is " + serverProtoVers);
+
+        /*
+         * Create a response.
+         */
+        String vmName = System.getProperty("java.vm.name", "?");
+        String vmVersion = System.getProperty("java.vm.version", "?");
+        String vmIdent = vmName + " v" + vmVersion;
+
+        //String appName = android.app.ActivityThread.currentPackageName();
+        //if (appName == null)
+        //    appName = "unknown";
+        String appName = DdmHandleAppName.getAppName();
+
+        VMRuntime vmRuntime = VMRuntime.getRuntime();
+        String instructionSetDescription =
+            vmRuntime.is64Bit() ? "64-bit" : "32-bit";
+        String vmInstructionSet = vmRuntime.vmInstructionSet();
+        if (vmInstructionSet != null && vmInstructionSet.length() > 0) {
+          instructionSetDescription += " (" + vmInstructionSet + ")";
+        }
+        String vmFlags = "CheckJNI="
+            + (vmRuntime.isCheckJniEnabled() ? "true" : "false");
+        boolean isNativeDebuggable = vmRuntime.isNativeDebuggable();
+
+        ByteBuffer out = ByteBuffer.allocate(28
+                            + vmIdent.length() * 2
+                            + appName.length() * 2
+                            + instructionSetDescription.length() * 2
+                            + vmFlags.length() * 2
+                            + 1);
+        out.order(ChunkHandler.CHUNK_ORDER);
+        out.putInt(CLIENT_PROTOCOL_VERSION);
+        out.putInt(android.os.Process.myPid());
+        out.putInt(vmIdent.length());
+        out.putInt(appName.length());
+        putString(out, vmIdent);
+        putString(out, appName);
+        out.putInt(UserHandle.myUserId());
+        out.putInt(instructionSetDescription.length());
+        putString(out, instructionSetDescription);
+        out.putInt(vmFlags.length());
+        putString(out, vmFlags);
+        out.put((byte)(isNativeDebuggable ? 1 : 0));
+
+        Chunk reply = new Chunk(CHUNK_HELO, out);
+
+        /*
+         * Take the opportunity to inform DDMS if we are waiting for a
+         * debugger to attach.
+         */
+        if (Debug.waitingForDebugger())
+            sendWAIT(0);
+
+        return reply;
+    }
+
+    /*
+     * Handle request for list of supported features.
+     */
+    private Chunk handleFEAT(Chunk request) {
+        // TODO: query the VM to ensure that support for these features
+        // is actually compiled in
+        final String[] vmFeatures = Debug.getVmFeatureList();
+
+        if (false)
+            Log.v("ddm-heap", "Got feature list request");
+
+        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 = 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 + FRAMEWORK_FEATURES.length);
+        for (int i = vmFeatures.length-1; i >= 0; i--) {
+            out.putInt(vmFeatures[i].length());
+            putString(out, vmFeatures[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);
+    }
+
+    /**
+     * Send up a WAIT chunk.  The only currently defined value for "reason"
+     * is zero, which means "waiting for a debugger".
+     */
+    public static void sendWAIT(int reason) {
+        byte[] data = new byte[] { (byte) reason };
+        Chunk waitChunk = new Chunk(CHUNK_WAIT, data, 0, 1);
+        DdmServer.sendChunk(waitChunk);
+    }
+}
+
diff --git a/android/ddm/DdmHandleNativeHeap.java b/android/ddm/DdmHandleNativeHeap.java
new file mode 100644
index 0000000..775c570
--- /dev/null
+++ b/android/ddm/DdmHandleNativeHeap.java
@@ -0,0 +1,91 @@
+/*
+ * 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 android.ddm;
+
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+import android.util.Log;
+
+/**
+ * Handle thread-related traffic.
+ */
+public class DdmHandleNativeHeap extends ChunkHandler {
+
+    public static final int CHUNK_NHGT = type("NHGT");
+
+    private static DdmHandleNativeHeap mInstance = new DdmHandleNativeHeap();
+
+
+    /* singleton, do not instantiate */
+    private DdmHandleNativeHeap() {}
+
+    /**
+     * Register for the messages we're interested in.
+     */
+    public static void register() {
+        DdmServer.registerHandler(CHUNK_NHGT, mInstance);
+    }
+
+    /**
+     * Called when the DDM server connects.  The handler is allowed to
+     * send messages to the server.
+     */
+    public void connected() {}
+
+    /**
+     * Called when the DDM server disconnects.  Can be used to disable
+     * periodic transmissions or clean up saved state.
+     */
+    public void disconnected() {}
+
+    /**
+     * Handle a chunk of data.
+     */
+    public Chunk handleChunk(Chunk request) {
+        Log.i("ddm-nativeheap", "Handling " + name(request.type) + " chunk");
+        int type = request.type;
+
+        if (type == CHUNK_NHGT) {
+            return handleNHGT(request);
+        } else {
+            throw new RuntimeException("Unknown packet "
+                + ChunkHandler.name(type));
+        }
+    }
+
+    /*
+     * Handle a "Native Heap GeT" request.
+     */
+    private Chunk handleNHGT(Chunk request) {
+        //ByteBuffer in = wrapChunk(request);
+
+        byte[] data = getLeakInfo();
+
+        if (data != null) {
+            // wrap & return
+            Log.i("ddm-nativeheap", "Sending " + data.length + " bytes");
+            return new Chunk(ChunkHandler.type("NHGT"), data, 0, data.length);
+        } else {
+            // failed, return a failure error code and message
+            return createFailChunk(1, "Something went wrong");
+        }
+    }
+    
+    private native byte[] getLeakInfo();
+}
+
diff --git a/android/ddm/DdmHandleProfiling.java b/android/ddm/DdmHandleProfiling.java
new file mode 100644
index 0000000..cce4dd2
--- /dev/null
+++ b/android/ddm/DdmHandleProfiling.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2009 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 org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+import android.os.Debug;
+import android.util.Log;
+import java.nio.ByteBuffer;
+
+/**
+ * Handle profiling requests.
+ */
+public class DdmHandleProfiling extends ChunkHandler {
+
+    public static final int CHUNK_MPRS = type("MPRS");
+    public static final int CHUNK_MPRE = type("MPRE");
+    public static final int CHUNK_MPSS = type("MPSS");
+    public static final int CHUNK_MPSE = type("MPSE");
+    public static final int CHUNK_MPRQ = type("MPRQ");
+    public static final int CHUNK_SPSS = type("SPSS");
+    public static final int CHUNK_SPSE = type("SPSE");
+
+    private static final boolean DEBUG = false;
+    private static DdmHandleProfiling mInstance = new DdmHandleProfiling();
+
+
+    /* singleton, do not instantiate */
+    private DdmHandleProfiling() {}
+
+    /**
+     * Register for the messages we're interested in.
+     */
+    public static void register() {
+        DdmServer.registerHandler(CHUNK_MPRS, mInstance);
+        DdmServer.registerHandler(CHUNK_MPRE, mInstance);
+        DdmServer.registerHandler(CHUNK_MPSS, mInstance);
+        DdmServer.registerHandler(CHUNK_MPSE, mInstance);
+        DdmServer.registerHandler(CHUNK_MPRQ, mInstance);
+        DdmServer.registerHandler(CHUNK_SPSS, mInstance);
+        DdmServer.registerHandler(CHUNK_SPSE, mInstance);
+    }
+
+    /**
+     * Called when the DDM server connects.  The handler is allowed to
+     * send messages to the server.
+     */
+    public void connected() {}
+
+    /**
+     * Called when the DDM server disconnects.  Can be used to disable
+     * periodic transmissions or clean up saved state.
+     */
+    public void disconnected() {}
+
+    /**
+     * Handle a chunk of data.
+     */
+    public Chunk handleChunk(Chunk request) {
+        if (DEBUG)
+            Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
+        int type = request.type;
+
+        if (type == CHUNK_MPRS) {
+            return handleMPRS(request);
+        } else if (type == CHUNK_MPRE) {
+            return handleMPRE(request);
+        } else if (type == CHUNK_MPSS) {
+            return handleMPSS(request);
+        } else if (type == CHUNK_MPSE) {
+            return handleMPSEOrSPSE(request, "Method");
+        } else if (type == CHUNK_MPRQ) {
+            return handleMPRQ(request);
+        } else if (type == CHUNK_SPSS) {
+            return handleSPSS(request);
+        } else if (type == CHUNK_SPSE) {
+            return handleMPSEOrSPSE(request, "Sample");
+        } else {
+            throw new RuntimeException("Unknown packet "
+                + ChunkHandler.name(type));
+        }
+    }
+
+    /*
+     * Handle a "Method PRofiling Start" request.
+     */
+    private Chunk handleMPRS(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+
+        int bufferSize = in.getInt();
+        int flags = in.getInt();
+        int len = in.getInt();
+        String fileName = getString(in, len);
+        if (DEBUG)
+            Log.v("ddm-heap", "Method profiling start: filename='" + fileName
+                + "', size=" + bufferSize + ", flags=" + flags);
+
+        try {
+            Debug.startMethodTracing(fileName, bufferSize, flags);
+            return null;        // empty response
+        } catch (RuntimeException re) {
+            return createFailChunk(1, re.getMessage());
+        }
+    }
+
+    /*
+     * Handle a "Method PRofiling End" request.
+     */
+    private Chunk handleMPRE(Chunk request) {
+        byte result;
+
+        try {
+            Debug.stopMethodTracing();
+            result = 0;
+        } catch (RuntimeException re) {
+            Log.w("ddm-heap", "Method profiling end failed: "
+                + re.getMessage());
+            result = 1;
+        }
+
+        /* create a non-empty reply so the handler fires on completion */
+        byte[] reply = { result };
+        return new Chunk(CHUNK_MPRE, reply, 0, reply.length);
+    }
+
+    /*
+     * Handle a "Method Profiling w/Streaming Start" request.
+     */
+    private Chunk handleMPSS(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+
+        int bufferSize = in.getInt();
+        int flags = in.getInt();
+        if (DEBUG) {
+            Log.v("ddm-heap", "Method prof stream start: size=" + bufferSize
+                + ", flags=" + flags);
+        }
+
+        try {
+            Debug.startMethodTracingDdms(bufferSize, flags, false, 0);
+            return null;        // empty response
+        } catch (RuntimeException re) {
+            return createFailChunk(1, re.getMessage());
+        }
+    }
+
+    /*
+     * Handle a "Method Profiling w/Streaming End" request or a
+     * "Sample Profiling w/Streaming End" request.
+     */
+    private Chunk handleMPSEOrSPSE(Chunk request, String type) {
+        if (DEBUG) {
+            Log.v("ddm-heap", type + " prof stream end");
+        }
+
+        try {
+            Debug.stopMethodTracing();
+        } catch (RuntimeException re) {
+            Log.w("ddm-heap", type + " prof stream end failed: "
+                + re.getMessage());
+            return createFailChunk(1, re.getMessage());
+        }
+
+        /* VM sent the (perhaps very large) response directly */
+        return null;
+    }
+
+    /*
+     * Handle a "Method PRofiling Query" request.
+     */
+    private Chunk handleMPRQ(Chunk request) {
+        int result = Debug.getMethodTracingMode();
+
+        /* create a non-empty reply so the handler fires on completion */
+        byte[] reply = { (byte) result };
+        return new Chunk(CHUNK_MPRQ, reply, 0, reply.length);
+    }
+
+    /*
+     * Handle a "Sample Profiling w/Streaming Start" request.
+     */
+    private Chunk handleSPSS(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+
+        int bufferSize = in.getInt();
+        int flags = in.getInt();
+        int interval = in.getInt();
+        if (DEBUG) {
+            Log.v("ddm-heap", "Sample prof stream start: size=" + bufferSize
+                + ", flags=" + flags + ", interval=" + interval);
+        }
+
+        try {
+            Debug.startMethodTracingDdms(bufferSize, flags, true, interval);
+            return null;        // empty response
+        } catch (RuntimeException re) {
+            return createFailChunk(1, re.getMessage());
+        }
+    }
+}
+
diff --git a/android/ddm/DdmHandleThread.java b/android/ddm/DdmHandleThread.java
new file mode 100644
index 0000000..613ab75
--- /dev/null
+++ b/android/ddm/DdmHandleThread.java
@@ -0,0 +1,181 @@
+/*
+ * 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 android.ddm;
+
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
+import android.util.Log;
+import java.nio.ByteBuffer;
+
+/**
+ * Handle thread-related traffic.
+ */
+public class DdmHandleThread extends ChunkHandler {
+
+    public static final int CHUNK_THEN = type("THEN");
+    public static final int CHUNK_THCR = type("THCR");
+    public static final int CHUNK_THDE = type("THDE");
+    public static final int CHUNK_THST = type("THST");
+    public static final int CHUNK_STKL = type("STKL");
+
+    private static DdmHandleThread mInstance = new DdmHandleThread();
+
+
+    /* singleton, do not instantiate */
+    private DdmHandleThread() {}
+
+    /**
+     * Register for the messages we're interested in.
+     */
+    public static void register() {
+        DdmServer.registerHandler(CHUNK_THEN, mInstance);
+        DdmServer.registerHandler(CHUNK_THST, mInstance);
+        DdmServer.registerHandler(CHUNK_STKL, mInstance);
+    }
+
+    /**
+     * Called when the DDM server connects.  The handler is allowed to
+     * send messages to the server.
+     */
+    public void connected() {}
+
+    /**
+     * Called when the DDM server disconnects.  Can be used to disable
+     * periodic transmissions or clean up saved state.
+     */
+    public void disconnected() {}
+
+    /**
+     * Handle a chunk of data.
+     */
+    public Chunk handleChunk(Chunk request) {
+        if (false)
+            Log.v("ddm-thread", "Handling " + name(request.type) + " chunk");
+        int type = request.type;
+
+        if (type == CHUNK_THEN) {
+            return handleTHEN(request);
+        } else if (type == CHUNK_THST) {
+            return handleTHST(request);
+        } else if (type == CHUNK_STKL) {
+            return handleSTKL(request);
+        } else {
+            throw new RuntimeException("Unknown packet "
+                + ChunkHandler.name(type));
+        }
+    }
+
+    /*
+     * Handle a "THread notification ENable" request.
+     */
+    private Chunk handleTHEN(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+
+        boolean enable = (in.get() != 0);
+        //Log.i("ddm-thread", "Thread notify enable: " + enable);
+
+        DdmVmInternal.threadNotify(enable);
+        return null;        // empty response
+    }
+
+    /*
+     * Handle a "THread STatus" request.  This is constructed by the VM.
+     */
+    private Chunk handleTHST(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+        // currently nothing to read from "in"
+
+        //Log.d("ddm-thread", "Thread status request");
+
+        byte[] status = DdmVmInternal.getThreadStats();
+        if (status != null)
+            return new Chunk(CHUNK_THST, status, 0, status.length);
+        else
+            return createFailChunk(1, "Can't build THST chunk");
+    }
+
+    /*
+     * Handle a STacK List request.
+     *
+     * This is done by threadId, which isn't great since those are
+     * recycled.  We need a thread serial ID.  The Linux tid is an okay
+     * answer as it's unlikely to recycle at the exact wrong moment.
+     * However, we're using the short threadId in THST messages, so we
+     * use them here for consistency.  (One thought is to keep the current
+     * thread ID in the low 16 bits and somehow serialize the top 16 bits.)
+     */
+    private Chunk handleSTKL(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+        int threadId;
+
+        threadId = in.getInt();
+
+        //Log.d("ddm-thread", "Stack list request " + threadId);
+
+        StackTraceElement[] trace = DdmVmInternal.getStackTraceById(threadId);
+        if (trace == null) {
+            return createFailChunk(1, "Stack trace unavailable");
+        } else {
+            return createStackChunk(trace, threadId);
+        }
+    }
+
+    /*
+     * Serialize a StackTraceElement[] into an STKL chunk.
+     *
+     * We include the threadId in the response so the other side doesn't have
+     * to match up requests and responses as carefully.
+     */
+    private Chunk createStackChunk(StackTraceElement[] trace, int threadId) {
+        int bufferSize = 0;
+
+        bufferSize += 4;            // version, flags, whatever
+        bufferSize += 4;            // thread ID
+        bufferSize += 4;            // frame count
+        for (StackTraceElement elem : trace) {
+            bufferSize += 4 + elem.getClassName().length() * 2;
+            bufferSize += 4 + elem.getMethodName().length() * 2;
+            bufferSize += 4;
+            if (elem.getFileName() != null)
+                bufferSize += elem.getFileName().length() * 2;
+            bufferSize += 4;        // line number
+        }
+
+        ByteBuffer out = ByteBuffer.allocate(bufferSize);
+        out.putInt(0);
+        out.putInt(threadId);
+        out.putInt(trace.length);
+        for (StackTraceElement elem : trace) {
+            out.putInt(elem.getClassName().length());
+            putString(out, elem.getClassName());
+            out.putInt(elem.getMethodName().length());
+            putString(out, elem.getMethodName());
+            if (elem.getFileName() != null) {
+                out.putInt(elem.getFileName().length());
+                putString(out, elem.getFileName());
+            } else {
+                out.putInt(0);
+            }
+            out.putInt(elem.getLineNumber());
+        }
+
+        return new Chunk(CHUNK_STKL, out);
+    }
+}
+
diff --git a/android/ddm/DdmHandleViewDebug.java b/android/ddm/DdmHandleViewDebug.java
new file mode 100644
index 0000000..5539dc9
--- /dev/null
+++ b/android/ddm/DdmHandleViewDebug.java
@@ -0,0 +1,434 @@
+/*
+ * 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.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.lang.reflect.Method;
+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 {
+    /** 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;
+
+    /** 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.
+     */
+    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;
+
+    /** 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;
+
+    /** 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;
+
+    /** 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. */
+    private DdmHandleViewDebug() {}
+
+    public static void register() {
+        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_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 if (op == VURT_DUMP_THEME)
+                return dumpTheme(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_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);
+            }
+        } else {
+            throw new RuntimeException("Unknown packet " + ChunkHandler.name(type));
+        }
+    }
+
+    /** 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;
+        boolean v2 = in.hasRemaining() && in.getInt() > 0;
+
+        long start = System.currentTimeMillis();
+
+        ByteArrayOutputStream b = new ByteArrayOutputStream(2*1024*1024);
+        try {
+            if (v2) {
+                ViewDebug.dumpv2(rootView, b);
+            } else {
+                ViewDebug.dump(rootView, skipChildren, includeProperties, b);
+            }
+        } catch (IOException | InterruptedException e) {
+            return createFailChunk(1, "Unexpected error while obtaining view hierarchy: "
+                    + e.getMessage());
+        }
+
+        long end = System.currentTimeMillis();
+        Log.d(TAG, "Time to obtain view hierarchy (ms): " + (end - start));
+
+        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);
+    }
+
+    /**
+     * Returns the Theme dump of the provided view.
+     */
+    private Chunk dumpTheme(View rootView) {
+        ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
+        try {
+            ViewDebug.dumpTheme(rootView, b);
+        } catch (IOException e) {
+            return createFailChunk(1, "Unexpected error while dumping the theme: "
+                    + e.getMessage());
+        }
+
+        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;
+    }
+
+    /**
+     * 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;
+    }
+
+    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;
+    }
+
+    /** 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/android/ddm/DdmRegister.java b/android/ddm/DdmRegister.java
new file mode 100644
index 0000000..e0faa51
--- /dev/null
+++ b/android/ddm/DdmRegister.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.ddm;
+
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+import android.util.Log;
+
+/**
+ * Just a place to stick handler registrations, instead of scattering
+ * them around.
+ */
+public class DdmRegister {
+
+    private DdmRegister() {}
+
+    /**
+     * Register handlers for all known chunk types.
+     *
+     * If you write a handler, add a registration call here.
+     *
+     * Note that this is invoked by the application (usually through a
+     * static initializer in the main class), not the VM.  It's done this
+     * way so that the handlers can use Android classes with native calls
+     * that aren't registered until after the VM is initialized (e.g.
+     * logging).  It also allows debugging of DDM handler initialization.
+     *
+     * The chunk dispatcher will pause until we call registrationComplete(),
+     * so that we don't have a race that causes us to drop packets before
+     * we finish here.
+     */
+    public static void registerHandlers() {
+        if (false)
+            Log.v("ddm", "Registering DDM message handlers");
+        DdmHandleHello.register();
+        DdmHandleThread.register();
+        DdmHandleHeap.register();
+        DdmHandleNativeHeap.register();
+        DdmHandleProfiling.register();
+        DdmHandleExit.register();
+        DdmHandleViewDebug.register();
+
+        DdmServer.registrationComplete();
+    }
+}
+