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
17package android.ddm;
18
19import org.apache.harmony.dalvik.ddmc.Chunk;
20import org.apache.harmony.dalvik.ddmc.ChunkHandler;
21import org.apache.harmony.dalvik.ddmc.DdmServer;
22import android.util.Log;
23import android.os.Debug;
24import android.os.UserHandle;
25
26import java.nio.ByteBuffer;
27
28/**
29 * Handle "hello" messages and feature discovery.
30 */
31public class DdmHandleHello extends ChunkHandler {
32
33    public static final int CHUNK_HELO = type("HELO");
34    public static final int CHUNK_WAIT = type("WAIT");
35    public static final int CHUNK_FEAT = type("FEAT");
36
37    private static DdmHandleHello mInstance = new DdmHandleHello();
38
39
40    /* singleton, do not instantiate */
41    private DdmHandleHello() {}
42
43    /**
44     * Register for the messages we're interested in.
45     */
46    public static void register() {
47        DdmServer.registerHandler(CHUNK_HELO, mInstance);
48        DdmServer.registerHandler(CHUNK_FEAT, mInstance);
49    }
50
51    /**
52     * Called when the DDM server connects.  The handler is allowed to
53     * send messages to the server.
54     */
55    public void connected() {
56        if (false)
57            Log.v("ddm-hello", "Connected!");
58
59        if (false) {
60            /* test spontaneous transmission */
61            byte[] data = new byte[] { 0, 1, 2, 3, 4, -4, -3, -2, -1, 127 };
62            Chunk testChunk =
63                new Chunk(ChunkHandler.type("TEST"), data, 1, data.length-2);
64            DdmServer.sendChunk(testChunk);
65        }
66    }
67
68    /**
69     * Called when the DDM server disconnects.  Can be used to disable
70     * periodic transmissions or clean up saved state.
71     */
72    public void disconnected() {
73        if (false)
74            Log.v("ddm-hello", "Disconnected!");
75    }
76
77    /**
78     * Handle a chunk of data.
79     */
80    public Chunk handleChunk(Chunk request) {
81        if (false)
82            Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
83        int type = request.type;
84
85        if (type == CHUNK_HELO) {
86            return handleHELO(request);
87        } else if (type == CHUNK_FEAT) {
88            return handleFEAT(request);
89        } else {
90            throw new RuntimeException("Unknown packet "
91                + ChunkHandler.name(type));
92        }
93    }
94
95    /*
96     * Handle introductory packet.
97     */
98    private Chunk handleHELO(Chunk request) {
99        if (false)
100            return createFailChunk(123, "This is a test");
101
102        /*
103         * Process the request.
104         */
105        ByteBuffer in = wrapChunk(request);
106
107        int serverProtoVers = in.getInt();
108        if (false)
109            Log.v("ddm-hello", "Server version is " + serverProtoVers);
110
111        /*
112         * Create a response.
113         */
114        String vmName = System.getProperty("java.vm.name", "?");
115        String vmVersion = System.getProperty("java.vm.version", "?");
116        String vmIdent = vmName + " v" + vmVersion;
117
118        //String appName = android.app.ActivityThread.currentPackageName();
119        //if (appName == null)
120        //    appName = "unknown";
121        String appName = DdmHandleAppName.getAppName();
122
123        ByteBuffer out = ByteBuffer.allocate(20
124                            + vmIdent.length()*2 + appName.length()*2);
125        out.order(ChunkHandler.CHUNK_ORDER);
126        out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION);
127        out.putInt(android.os.Process.myPid());
128        out.putInt(vmIdent.length());
129        out.putInt(appName.length());
130        putString(out, vmIdent);
131        putString(out, appName);
132        out.putInt(UserHandle.myUserId());
133
134        Chunk reply = new Chunk(CHUNK_HELO, out);
135
136        /*
137         * Take the opportunity to inform DDMS if we are waiting for a
138         * debugger to attach.
139         */
140        if (Debug.waitingForDebugger())
141            sendWAIT(0);
142
143        return reply;
144    }
145
146    /*
147     * Handle request for list of supported features.
148     */
149    private Chunk handleFEAT(Chunk request) {
150        // TODO: query the VM to ensure that support for these features
151        // is actually compiled in
152        final String[] features = Debug.getVmFeatureList();
153
154        if (false)
155            Log.v("ddm-heap", "Got feature list request");
156
157        int size = 4 + 4 * features.length;
158        for (int i = features.length-1; i >= 0; i--)
159            size += features[i].length() * 2;
160
161        ByteBuffer out = ByteBuffer.allocate(size);
162        out.order(ChunkHandler.CHUNK_ORDER);
163        out.putInt(features.length);
164        for (int i = features.length-1; i >= 0; i--) {
165            out.putInt(features[i].length());
166            putString(out, features[i]);
167        }
168
169        return new Chunk(CHUNK_FEAT, out);
170    }
171
172    /**
173     * Send up a WAIT chunk.  The only currently defined value for "reason"
174     * is zero, which means "waiting for a debugger".
175     */
176    public static void sendWAIT(int reason) {
177        byte[] data = new byte[] { (byte) reason };
178        Chunk waitChunk = new Chunk(CHUNK_WAIT, data, 0, 1);
179        DdmServer.sendChunk(waitChunk);
180    }
181}
182
183