summaryrefslogtreecommitdiff
path: root/android/ddm
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
committerJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
commit10d07c88d69cc64f73a069163e7ea5ba2519a099 (patch)
tree8dbd149eb350320a29c3d10e7ad3201de1c5cbee /android/ddm
parent677516fb6b6f207d373984757d3d9450474b6b00 (diff)
downloadandroid-28-10d07c88d69cc64f73a069163e7ea5ba2519a099.tar.gz
Import Android SDK Platform PI [4335822]
/google/data/ro/projects/android/fetch_artifact \ --bid 4335822 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4335822.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
Diffstat (limited to 'android/ddm')
-rw-r--r--android/ddm/DdmHandleAppName.java107
-rw-r--r--android/ddm/DdmHandleExit.java77
-rw-r--r--android/ddm/DdmHandleHeap.java269
-rw-r--r--android/ddm/DdmHandleHello.java215
-rw-r--r--android/ddm/DdmHandleNativeHeap.java91
-rw-r--r--android/ddm/DdmHandleProfiling.java216
-rw-r--r--android/ddm/DdmHandleThread.java181
-rw-r--r--android/ddm/DdmHandleViewDebug.java434
-rw-r--r--android/ddm/DdmRegister.java59
9 files changed, 1649 insertions, 0 deletions
diff --git a/android/ddm/DdmHandleAppName.java b/android/ddm/DdmHandleAppName.java
new file mode 100644
index 00000000..7e39e47c
--- /dev/null
+++ b/android/ddm/DdmHandleAppName.java
@@ -0,0 +1,107 @@
+/*
+ * 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;
+
+
+/**
+ * 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.
+ */
+ 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);
+ }
+
+ 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 00000000..74ae37a4
--- /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 00000000..e24aeb2a
--- /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 00000000..b2288fc4
--- /dev/null
+++ b/android/ddm/DdmHandleHello.java
@@ -0,0 +1,215 @@
+/*
+ * 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 android.os.Debug;
+import android.os.UserHandle;
+import dalvik.system.VMRuntime;
+
+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 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(DdmServer.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 00000000..775c5702
--- /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 00000000..cce4dd20
--- /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 00000000..613ab75f
--- /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 00000000..5539dc92
--- /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 00000000..e0faa51a
--- /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();
+ }
+}
+