• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  ** Copyright 2011, 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 com.android.ide.eclipse.gldebugger;
18 
19 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message;
20 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message.Function;
21 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message.Type;
22 import com.android.sdklib.util.SparseArray;
23 
24 import java.io.DataInputStream;
25 import java.io.DataOutputStream;
26 import java.io.EOFException;
27 import java.io.FileInputStream;
28 import java.io.IOException;
29 import java.net.Socket;
30 import java.nio.ByteOrder;
31 import java.util.ArrayList;
32 
33 abstract interface ProcessMessage {
processMessage(final MessageQueue queue, final Message msg)34     abstract boolean processMessage(final MessageQueue queue, final Message msg)
35             throws IOException;
36 }
37 
38 public class MessageQueue implements Runnable {
39 
40     private boolean running = false;
41     private ByteOrder byteOrder;
42     private FileInputStream file; // if null, create and use socket
43     Thread thread = null;
44     private final ProcessMessage[] processes;
45     private ArrayList<Message> complete = new ArrayList<Message>(); // synchronized
46     private ArrayList<Message> commands = new ArrayList<Message>(); // synchronized
47     private SampleView sampleView;
48 
MessageQueue(SampleView sampleView, final ProcessMessage[] processes)49     public MessageQueue(SampleView sampleView, final ProcessMessage[] processes) {
50         this.sampleView = sampleView;
51         this.processes = processes;
52     }
53 
start(final ByteOrder byteOrder, final FileInputStream file)54     public void start(final ByteOrder byteOrder, final FileInputStream file) {
55         if (running)
56             return;
57         running = true;
58         this.byteOrder = byteOrder;
59         this.file = file;
60         thread = new Thread(this);
61         thread.start();
62     }
63 
stop()64     public void stop() {
65         if (!running)
66             return;
67         running = false;
68     }
69 
isRunning()70     public boolean isRunning() {
71         return running;
72     }
73 
sendCommands(final int contextId)74     private void sendCommands(final int contextId) throws IOException {
75         synchronized (commands) {
76             for (int i = 0; i < commands.size(); i++) {
77                 Message command = commands.get(i);
78                 if (command.getContextId() == contextId || command.getContextId() == 0) {
79                     sendMessage(commands.remove(i));
80                     i--;
81                 }
82             }
83         }
84     }
85 
addCommand(Message command)86     public void addCommand(Message command) {
87         synchronized (commands) {
88             commands.add(command);
89         }
90     }
91 
92     // these should only be accessed from the network thread;
93     // access call chain starts with run()
94     private DataInputStream dis = null;
95     private DataOutputStream dos = null;
96     private SparseArray<ArrayList<Message>> incoming = new SparseArray<ArrayList<Message>>();
97 
run()98     public void run() {
99         Socket socket = null;
100         if (file == null)
101             try {
102                 socket = new Socket();
103                 socket.connect(new java.net.InetSocketAddress("127.0.0.1", Integer
104                         .parseInt(sampleView.actionPort.getText())));
105                 dis = new DataInputStream(socket.getInputStream());
106                 dos = new DataOutputStream(socket.getOutputStream());
107             } catch (Exception e) {
108                 running = false;
109                 Error(e);
110             }
111         else
112             dis = new DataInputStream(file);
113 
114         while (running) {
115             try {
116                 if (file != null && file.available() == 0) {
117                     running = false;
118                     break;
119                 }
120             } catch (IOException e1) {
121                 e1.printStackTrace();
122                 assert false;
123             }
124 
125             Message msg = null;
126             if (incoming.size() > 0) { // find queued incoming
127                 for (int i = 0; i < incoming.size(); i++) {
128                     final ArrayList<Message> messages = incoming.valueAt(i);
129                     if (messages.size() > 0) {
130                         msg = messages.remove(0);
131                         break;
132                     }
133                 }
134             }
135             try {
136                 if (null == msg) // get incoming from network
137                     msg = receiveMessage(dis);
138                 processMessage(dos, msg);
139             } catch (IOException e) {
140                 Error(e);
141                 running = false;
142                 break;
143             }
144         }
145 
146         try {
147             if (socket != null)
148                 socket.close();
149             else
150                 file.close();
151         } catch (IOException e) {
152             Error(e);
153             running = false;
154         }
155 
156     }
157 
putMessage(final Message msg)158     private void putMessage(final Message msg) {
159         ArrayList<Message> existing = incoming.get(msg.getContextId());
160         if (existing == null)
161             incoming.put(msg.getContextId(), existing = new ArrayList<Message>());
162         existing.add(msg);
163     }
164 
receiveMessage(final int contextId)165     Message receiveMessage(final int contextId) throws IOException {
166         Message msg = receiveMessage(dis);
167         while (msg.getContextId() != contextId) {
168             putMessage(msg);
169             msg = receiveMessage(dis);
170         }
171         return msg;
172     }
173 
sendMessage(final Message msg)174     void sendMessage(final Message msg) throws IOException {
175         sendMessage(dos, msg);
176     }
177 
178     // should only be used by DefaultProcessMessage
179     private SparseArray<Message> partials = new SparseArray<Message>();
180 
getPartialMessage(final int contextId)181     Message getPartialMessage(final int contextId) {
182         return partials.get(contextId);
183     }
184 
185     // used to add BeforeCall to complete if it was skipped
completePartialMessage(final int contextId)186     void completePartialMessage(final int contextId) {
187         final Message msg = partials.get(contextId);
188         partials.remove(contextId);
189         assert msg != null;
190         assert msg.getType() == Type.BeforeCall;
191         if (msg != null)
192             synchronized (complete) {
193                 complete.add(msg);
194             }
195     }
196 
197     // can be used by other message processor as default processor
defaultProcessMessage(final Message msg, boolean expectResponse, boolean sendResponse)198     void defaultProcessMessage(final Message msg, boolean expectResponse,
199             boolean sendResponse) throws IOException {
200         final int contextId = msg.getContextId();
201         if (msg.getType() == Type.BeforeCall) {
202             if (sendResponse) {
203                 final Message.Builder builder = Message.newBuilder();
204                 builder.setContextId(contextId);
205                 builder.setType(Type.Response);
206                 builder.setExpectResponse(expectResponse);
207                 builder.setFunction(Function.CONTINUE);
208                 sendMessage(dos, builder.build());
209             }
210             assert partials.indexOfKey(contextId) < 0;
211             partials.put(contextId, msg);
212         } else if (msg.getType() == Type.AfterCall) {
213             if (sendResponse) {
214                 final Message.Builder builder = Message.newBuilder();
215                 builder.setContextId(contextId);
216                 builder.setType(Type.Response);
217                 builder.setExpectResponse(expectResponse);
218                 builder.setFunction(Function.SKIP);
219                 sendMessage(dos, builder.build());
220             }
221             assert partials.indexOfKey(contextId) >= 0;
222             final Message before = partials.get(contextId);
223             partials.remove(contextId);
224             assert before.getFunction() == msg.getFunction();
225             final Message completed = before.toBuilder().mergeFrom(msg)
226                     .setType(Type.CompleteCall).build();
227             synchronized (complete) {
228                 complete.add(completed);
229             }
230         } else if (msg.getType() == Type.CompleteCall) {
231             // this type should only be encountered on client after processing
232             assert file != null;
233             assert !msg.getExpectResponse();
234             assert !sendResponse;
235             assert partials.indexOfKey(contextId) < 0;
236             synchronized (complete) {
237                 complete.add(msg);
238             }
239         } else if (msg.getType() == Type.Response && msg.getFunction() == Function.SETPROP) {
240             synchronized (complete) {
241                 complete.add(msg);
242             }
243         } else
244             assert false;
245     }
246 
247     public Message removeCompleteMessage(int contextId) {
248         synchronized (complete) {
249             if (complete.size() == 0)
250                 return null;
251             if (0 == contextId) // get a message for any context
252                 return complete.remove(0);
253             for (int i = 0; i < complete.size(); i++) {
254                 Message msg = complete.get(i);
255                 if (msg.getContextId() == contextId) {
256                     complete.remove(i);
257                     return msg;
258                 }
259             }
260         }
261         return null;
262     }
263 
264     private Message receiveMessage(final DataInputStream dis)
265             throws IOException {
266         int len = 0;
267         try {
268             len = dis.readInt();
269             if (byteOrder == ByteOrder.LITTLE_ENDIAN)
270                 len = Integer.reverseBytes(len); // readInt reads BIT_ENDIAN
271         } catch (EOFException e) {
272             Error(new Exception("EOF"));
273         }
274         byte[] buffer = new byte[len];
275         int readLen = 0;
276         while (readLen < len) {
277             int read = -1;
278             try {
279                 read = dis.read(buffer, readLen, len - readLen);
280             } catch (EOFException e) {
281                 Error(new Exception("EOF"));
282             }
283             if (read < 0) {
284                 Error(new Exception("read length = " + read));
285                 return null;
286             } else
287                 readLen += read;
288         }
289         Message msg = Message.parseFrom(buffer);
290         sendCommands(msg.getContextId());
291         return msg;
292     }
293 
294     private void sendMessage(final DataOutputStream dos, final Message message)
295             throws IOException {
296         if (dos == null)
297             return;
298         assert message.getFunction() != Function.NEG;
299         final byte[] data = message.toByteArray();
300         if (byteOrder == ByteOrder.BIG_ENDIAN)
301             dos.writeInt(data.length);
302         else
303             dos.writeInt(Integer.reverseBytes(data.length));
304         dos.write(data);
305     }
306 
307     private void processMessage(final DataOutputStream dos, final Message msg) throws IOException {
308         if (msg.getExpectResponse()) {
309             assert dos != null; // readonly source cannot expectResponse
310             for (ProcessMessage process : processes)
311                 if (process.processMessage(this, msg))
312                     return;
313             defaultProcessMessage(msg, msg.getExpectResponse(), msg.getExpectResponse());
314         } else
315             defaultProcessMessage(msg, msg.getExpectResponse(), msg.getExpectResponse());
316     }
317 
318     void Error(Exception e) {
319         sampleView.showError(e);
320     }
321 }
322