diff options
author | Justin Klaassen <justinklaassen@google.com> | 2017-09-15 17:58:39 -0400 |
---|---|---|
committer | Justin Klaassen <justinklaassen@google.com> | 2017-09-15 17:58:39 -0400 |
commit | 10d07c88d69cc64f73a069163e7ea5ba2519a099 (patch) | |
tree | 8dbd149eb350320a29c3d10e7ad3201de1c5cbee /android/ddm | |
parent | 677516fb6b6f207d373984757d3d9450474b6b00 (diff) | |
download | android-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.java | 107 | ||||
-rw-r--r-- | android/ddm/DdmHandleExit.java | 77 | ||||
-rw-r--r-- | android/ddm/DdmHandleHeap.java | 269 | ||||
-rw-r--r-- | android/ddm/DdmHandleHello.java | 215 | ||||
-rw-r--r-- | android/ddm/DdmHandleNativeHeap.java | 91 | ||||
-rw-r--r-- | android/ddm/DdmHandleProfiling.java | 216 | ||||
-rw-r--r-- | android/ddm/DdmHandleThread.java | 181 | ||||
-rw-r--r-- | android/ddm/DdmHandleViewDebug.java | 434 | ||||
-rw-r--r-- | android/ddm/DdmRegister.java | 59 |
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(); + } +} + |