1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.ddm; 18 19 import android.os.DdmSyncState; 20 import android.os.Debug; 21 import android.os.UserHandle; 22 import android.util.Log; 23 24 import dalvik.system.VMRuntime; 25 26 import org.apache.harmony.dalvik.ddmc.Chunk; 27 import org.apache.harmony.dalvik.ddmc.ChunkHandler; 28 import org.apache.harmony.dalvik.ddmc.DdmServer; 29 30 import java.nio.ByteBuffer; 31 32 /** 33 * Handle "hello" messages and feature discovery. 34 */ 35 public class DdmHandleHello extends DdmHandle { 36 37 public static final int CHUNK_HELO = ChunkHandler.type("HELO"); 38 public static final int CHUNK_WAIT = ChunkHandler.type("WAIT"); 39 public static final int CHUNK_FEAT = ChunkHandler.type("FEAT"); 40 41 private static final int CLIENT_PROTOCOL_VERSION = 1; 42 43 private static DdmHandleHello mInstance = new DdmHandleHello(); 44 45 /* singleton, do not instantiate */ DdmHandleHello()46 private DdmHandleHello() {} 47 48 /** 49 * Register for the messages we're interested in. 50 */ register()51 public static void register() { 52 DdmServer.registerHandler(CHUNK_HELO, mInstance); 53 DdmServer.registerHandler(CHUNK_FEAT, mInstance); 54 } 55 56 /** 57 * Called when the DDM server connects. The handler is allowed to 58 * send messages to the server. 59 */ onConnected()60 public void onConnected() { 61 if (false) 62 Log.v("ddm-hello", "Connected!"); 63 64 if (false) { 65 /* test spontaneous transmission */ 66 byte[] data = new byte[] { 0, 1, 2, 3, 4, -4, -3, -2, -1, 127 }; 67 Chunk testChunk = new Chunk(ChunkHandler.type("TEST"), data, 1, data.length - 2); 68 DdmServer.sendChunk(testChunk); 69 } 70 } 71 72 /** 73 * Called when the DDM server disconnects. Can be used to disable 74 * periodic transmissions or clean up saved state. 75 */ onDisconnected()76 public void onDisconnected() { 77 if (false) 78 Log.v("ddm-hello", "Disconnected!"); 79 } 80 81 /** 82 * Handle a chunk of data. 83 */ handleChunk(Chunk request)84 public Chunk handleChunk(Chunk request) { 85 if (false) 86 Log.v("ddm-heap", "Handling " + name(request.type) + " chunk"); 87 int type = request.type; 88 89 if (type == CHUNK_HELO) { 90 return handleHELO(request); 91 } else if (type == CHUNK_FEAT) { 92 return handleFEAT(request); 93 } else { 94 throw new RuntimeException("Unknown packet " + name(type)); 95 } 96 } 97 98 /* 99 * Handle introductory packet. This is called during JNI_CreateJavaVM 100 * before frameworks native methods are registered, so be careful not 101 * to call any APIs that depend on frameworks native code. 102 */ handleHELO(Chunk request)103 private Chunk handleHELO(Chunk request) { 104 if (false) 105 return createFailChunk(123, "This is a test"); 106 107 /* 108 * Process the request. 109 */ 110 ByteBuffer in = wrapChunk(request); 111 112 int serverProtoVers = in.getInt(); 113 if (false) 114 Log.v("ddm-hello", "Server version is " + serverProtoVers); 115 116 /* 117 * Create a response. 118 */ 119 String vmName = System.getProperty("java.vm.name", "?"); 120 String vmVersion = System.getProperty("java.vm.version", "?"); 121 String vmIdent = vmName + " v" + vmVersion; 122 123 DdmHandleAppName.Names names = DdmHandleAppName.getNames(); 124 String appName = names.getAppName(); 125 String pkgName = names.getPkgName(); 126 127 VMRuntime vmRuntime = VMRuntime.getRuntime(); 128 String instructionSetDescription = 129 vmRuntime.is64Bit() ? "64-bit" : "32-bit"; 130 String vmInstructionSet = vmRuntime.vmInstructionSet(); 131 if (vmInstructionSet != null && vmInstructionSet.length() > 0) { 132 instructionSetDescription += " (" + vmInstructionSet + ")"; 133 } 134 String vmFlags = "CheckJNI=" 135 + (vmRuntime.isCheckJniEnabled() ? "true" : "false"); 136 boolean isNativeDebuggable = vmRuntime.isNativeDebuggable(); 137 138 ByteBuffer out = ByteBuffer.allocate(32 139 + vmIdent.length() * 2 140 + appName.length() * 2 141 + instructionSetDescription.length() * 2 142 + vmFlags.length() * 2 143 + 1 144 + pkgName.length() * 2 145 // STAG id (int) 146 + Integer.BYTES); 147 out.order(ChunkHandler.CHUNK_ORDER); 148 out.putInt(CLIENT_PROTOCOL_VERSION); 149 out.putInt(android.os.Process.myPid()); 150 out.putInt(vmIdent.length()); 151 out.putInt(appName.length()); 152 putString(out, vmIdent); 153 putString(out, appName); 154 out.putInt(UserHandle.myUserId()); 155 out.putInt(instructionSetDescription.length()); 156 putString(out, instructionSetDescription); 157 out.putInt(vmFlags.length()); 158 putString(out, vmFlags); 159 out.put((byte)(isNativeDebuggable ? 1 : 0)); 160 out.putInt(pkgName.length()); 161 putString(out, pkgName); 162 163 // Added API 34 (and advertised via FEAT ddm packet) 164 // Send the current boot stage in ActivityThread 165 out.putInt(DdmSyncState.getStage().toInt()); 166 167 Chunk reply = new Chunk(CHUNK_HELO, out); 168 169 /* 170 * Take the opportunity to inform DDMS if we are waiting for a 171 * debugger to attach. 172 */ 173 if (Debug.waitingForDebugger()) 174 sendWAIT(0); 175 176 return reply; 177 } 178 179 /* 180 * Handle request for list of supported features. 181 */ handleFEAT(Chunk request)182 private Chunk handleFEAT(Chunk request) { 183 // TODO: query the VM to ensure that support for these features 184 // is actually compiled in 185 final String[] vmFeatures = Debug.getVmFeatureList(); 186 187 if (false) 188 Log.v("ddm-heap", "Got feature list request"); 189 190 String[] fmFeatures = Debug.getFeatureList(); 191 int size = 4 + 4 * (vmFeatures.length + fmFeatures.length); 192 for (int i = vmFeatures.length - 1; i >= 0; i--) { 193 size += vmFeatures[i].length() * 2; 194 } 195 for (int i = fmFeatures.length - 1; i >= 0; i--) { 196 size += fmFeatures[i].length() * 2; 197 } 198 199 ByteBuffer out = ByteBuffer.allocate(size); 200 out.order(ChunkHandler.CHUNK_ORDER); 201 out.putInt(vmFeatures.length + fmFeatures.length); 202 for (int i = vmFeatures.length-1; i >= 0; i--) { 203 out.putInt(vmFeatures[i].length()); 204 putString(out, vmFeatures[i]); 205 } 206 for (int i = fmFeatures.length - 1; i >= 0; i--) { 207 out.putInt(fmFeatures[i].length()); 208 putString(out, fmFeatures[i]); 209 } 210 211 return new Chunk(CHUNK_FEAT, out); 212 } 213 214 /** 215 * Send up a WAIT chunk. The only currently defined value for "reason" 216 * is zero, which means "waiting for a debugger". 217 */ sendWAIT(int reason)218 public static void sendWAIT(int reason) { 219 byte[] data = new byte[] { (byte) reason }; 220 Chunk waitChunk = new Chunk(CHUNK_WAIT, data, 0, 1); 221 DdmServer.sendChunk(waitChunk); 222 } 223 } 224 225