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