SDL_Android/SmartDeviceLinkAndroidProxy - added the correct version of the proxy
[profile/ivi/smartdevicelink.git] / SDL_Android / SmartDeviceLinkProxyAndroid / src / com / smartdevicelink / protocol / SmartDeviceLinkProtocol.java
1 package com.smartdevicelink.protocol;\r
2 \r
3 import java.io.ByteArrayOutputStream;\r
4 import java.util.Hashtable;\r
5 \r
6 import android.util.Log;\r
7 \r
8 import com.smartdevicelink.exception.*;\r
9 import com.smartdevicelink.protocol.enums.*;\r
10 import com.smartdevicelink.util.BitConverter;\r
11 import com.smartdevicelink.util.DebugTool;\r
12 \r
13 public class SmartDeviceLinkProtocol extends AbstractProtocol {\r
14         byte _version = 1;\r
15         private final static String FailurePropagating_Msg = "Failure propagating ";\r
16 \r
17         private static final int MTU_SIZE = 1500;\r
18         private static int HEADER_SIZE = 8;\r
19         private static int MAX_DATA_SIZE = MTU_SIZE - HEADER_SIZE;\r
20 \r
21         boolean _haveHeader = false;\r
22         byte[] _headerBuf = new byte[HEADER_SIZE];\r
23         int _headerBufWritePos = 0;\r
24         ProtocolFrameHeader _currentHeader = null;\r
25         byte[] _dataBuf = null;\r
26         int _dataBufWritePos = 0;\r
27         \r
28         int hashID = 0;\r
29         int messageID = 0;\r
30         \r
31         Hashtable<Integer, MessageFrameAssembler> _assemblerForMessageID = new Hashtable<Integer, MessageFrameAssembler>();\r
32         Hashtable<Byte, Hashtable<Integer, MessageFrameAssembler>> _assemblerForSessionID = new Hashtable<Byte, Hashtable<Integer, MessageFrameAssembler>>();\r
33         Hashtable<Byte, Object> _messageLocks = new Hashtable<Byte, Object>();\r
34 \r
35         // Hide no-arg ctor\r
36         private SmartDeviceLinkProtocol() {\r
37                 super(null);\r
38         } // end-ctor\r
39 \r
40         public SmartDeviceLinkProtocol(IProtocolListener protocolListener) {\r
41                 super(protocolListener);\r
42         } // end-ctor\r
43         \r
44         public byte getVersion() {\r
45                 return this._version;\r
46         }\r
47         \r
48         public void setVersion(byte version) {\r
49                 this._version = version;\r
50                 if (version == 2) {\r
51                         HEADER_SIZE = 12;\r
52                         MAX_DATA_SIZE = MTU_SIZE - HEADER_SIZE;\r
53                         _headerBuf = new byte[HEADER_SIZE];\r
54                 }\r
55         }\r
56 \r
57         public void StartProtocolSession(SessionType sessionType) {\r
58                 ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createStartSession(sessionType, 0x00, _version);\r
59                 sendFrameToTransport(header);\r
60         } // end-method\r
61 \r
62         private void sendStartProtocolSessionACK(SessionType sessionType, byte sessionID) {\r
63                 ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createStartSessionACK(sessionType, sessionID, 0x00, _version);\r
64                 sendFrameToTransport(header);\r
65         } // end-method\r
66         \r
67         public void EndProtocolSession(SessionType sessionType, byte sessionID) {\r
68                 ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createEndSession(sessionType, sessionID, hashID, _version);\r
69                 //byte[] data = new byte[4];\r
70                 //data = BitConverter.intToByteArray(hashID);\r
71                 //handleProtocolFrameToSend(header, data, 0, data.length);\r
72                 sendFrameToTransport(header);\r
73         } // end-method\r
74 \r
75         public void SendMessage(ProtocolMessage protocolMsg) {  \r
76                 protocolMsg.setRPCType((byte) 0x00); //always sending a request\r
77                 SessionType sessionType = protocolMsg.getSessionType();\r
78                 byte sessionID = protocolMsg.getSessionID();\r
79                 \r
80                 byte[] data = null;\r
81                 if (_version == 2) {\r
82                         if (protocolMsg.getBulkData() != null) {\r
83                                 data = new byte[12 + protocolMsg.getJsonSize() + protocolMsg.getBulkData().length];\r
84                                 sessionType = SessionType.Bulk_Data;\r
85                         } else data = new byte[12 + protocolMsg.getJsonSize()];\r
86                         BinaryFrameHeader binFrameHeader = new BinaryFrameHeader();\r
87                         binFrameHeader = ProtocolFrameHeaderFactory.createBinaryFrameHeader(protocolMsg.getRPCType(), protocolMsg.getFunctionID(), protocolMsg.getCorrID(), protocolMsg.getJsonSize());\r
88                         System.arraycopy(binFrameHeader.assembleHeaderBytes(), 0, data, 0, 12);\r
89                         System.arraycopy(protocolMsg.getData(), 0, data, 12, protocolMsg.getJsonSize());\r
90                         if (protocolMsg.getBulkData() != null) {\r
91                                 System.arraycopy(protocolMsg.getBulkData(), 0, data, 12 + protocolMsg.getJsonSize(), protocolMsg.getBulkData().length);\r
92                         }\r
93                 } else {\r
94                         data = protocolMsg.getData();\r
95                 }\r
96                 \r
97                 // Get the message lock for this protocol session\r
98                 Object messageLock = _messageLocks.get(sessionID);\r
99                 if (messageLock == null) {\r
100                         handleProtocolError("Error sending protocol message to SMARTDEVICELINK.", \r
101                                         new SmartDeviceLinkException("Attempt to send protocol message prior to startSession ACK.", SmartDeviceLinkExceptionCause.SMARTDEVICELINK_UNAVAILALBE));\r
102                         return;\r
103                 }\r
104                 \r
105                 synchronized(messageLock) {\r
106                         if (data.length > MAX_DATA_SIZE) {\r
107                                 \r
108                                 messageID++;\r
109                                 ProtocolFrameHeader firstHeader = ProtocolFrameHeaderFactory.createMultiSendDataFirst(sessionType, sessionID, messageID, _version);\r
110         \r
111                                 // Assemble first frame.\r
112                                 int frameCount = data.length / MAX_DATA_SIZE;\r
113                                 if (data.length % MAX_DATA_SIZE > 0) {\r
114                                         frameCount++;\r
115                                 }\r
116                                 //byte[] firstFrameData = new byte[HEADER_SIZE];\r
117                                 byte[] firstFrameData = new byte[8];\r
118                                 // First four bytes are data size.\r
119                                 System.arraycopy(BitConverter.intToByteArray(data.length), 0, firstFrameData, 0, 4);\r
120                                 // Second four bytes are frame count.\r
121                                 System.arraycopy(BitConverter.intToByteArray(frameCount), 0, firstFrameData, 4, 4);\r
122                                 \r
123                                 handleProtocolFrameToSend(firstHeader, firstFrameData, 0, firstFrameData.length);\r
124                                 \r
125                                 int currentOffset = 0;\r
126                                 byte frameSequenceNumber = 0;\r
127                                 \r
128                                 for (int i = 0; i < frameCount; i++) {\r
129                                         if (i < (frameCount - 1)) {\r
130                                                 frameSequenceNumber = (byte)(i + 1);\r
131                                         } else {\r
132                                                 frameSequenceNumber = ProtocolFrameHeader.FrameDataFinalConsecutiveFrame;\r
133                                         } // end-if\r
134                                         \r
135                                         int bytesToWrite = data.length - currentOffset;\r
136                                         if (bytesToWrite > MAX_DATA_SIZE) { \r
137                                                 bytesToWrite = MAX_DATA_SIZE; \r
138                                         }\r
139 \r
140                                         ProtocolFrameHeader consecHeader = ProtocolFrameHeaderFactory.createMultiSendDataRest(sessionType, sessionID, bytesToWrite, frameSequenceNumber , messageID, _version);\r
141                                         handleProtocolFrameToSend(consecHeader, data, currentOffset, bytesToWrite);\r
142                                         currentOffset += bytesToWrite;\r
143                                 }\r
144                         } else {\r
145                                 messageID++;\r
146                                 ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createSingleSendData(sessionType, sessionID, data.length, messageID, _version);\r
147                                 handleProtocolFrameToSend(header, data, 0, data.length);\r
148                         }\r
149                 }\r
150         }\r
151 \r
152         private void sendFrameToTransport(ProtocolFrameHeader header) {\r
153                 handleProtocolFrameToSend(header, null, 0, 0);\r
154         }\r
155 \r
156         public void HandleReceivedBytes(byte[] receivedBytes, int receivedBytesLength) {\r
157                 int receivedBytesReadPos = 0;\r
158                 \r
159                 //Check for a version difference\r
160                 if (_version == 1) {\r
161                         //Nothing has been read into the buffer and version is 2\r
162                         if (_headerBufWritePos == 0 && (byte) (receivedBytes[0] >>> 4) == 2) {\r
163                                 setVersion((byte) (receivedBytes[0] >>> 4));\r
164                         //Buffer has something in it and version is 2\r
165                         } else if ((byte) (_headerBuf[0] >>> 4) == 2) {\r
166                                 //safe current state of the buffer and also set the new version\r
167                                 byte[] tempHeader = new byte[_headerBufWritePos];\r
168                                 tempHeader = _headerBuf;\r
169                                 setVersion((byte) (_headerBuf[0] >>> 4));\r
170                                 _headerBuf = tempHeader;\r
171                         }\r
172                 }\r
173                 \r
174                 // If I don't yet know the message size, grab those bytes.\r
175                 if (!_haveHeader) {\r
176                         // If I can't get the size, just get the bytes that are there.\r
177                         int headerBytesNeeded = _headerBuf.length - _headerBufWritePos;\r
178                         if (receivedBytesLength < headerBytesNeeded) {\r
179                                 System.arraycopy(receivedBytes, receivedBytesReadPos,\r
180                                                 _headerBuf, _headerBufWritePos, receivedBytesLength);\r
181                                 _headerBufWritePos += receivedBytesLength;\r
182                                 return;\r
183                         } else {\r
184                         // If I got the size, allocate the buffer\r
185                                 System.arraycopy(receivedBytes, receivedBytesReadPos,\r
186                                                 _headerBuf, _headerBufWritePos, headerBytesNeeded);\r
187                                 _headerBufWritePos += headerBytesNeeded;\r
188                                 receivedBytesReadPos += headerBytesNeeded;\r
189                                 _haveHeader = true;\r
190                                 _currentHeader  = ProtocolFrameHeader.parseSmartDeviceLinkProHeader(_headerBuf);\r
191                                 \r
192                                 \r
193                                 int iDataSize = _currentHeader.getDataSize();   \r
194 \r
195                                 if (iDataSize <= 4000)\r
196                                 {\r
197                                         _dataBuf = new byte[iDataSize];\r
198                                 }\r
199                                 else\r
200                                 {\r
201                                         //something is wrong with the header\r
202                                         Log.e("HandleReceivedBytes", "Corrupt header found, request to allocate a byte array of size: " + iDataSize);   \r
203                                         Log.e("HandleReceivedBytes", "_headerBuf: " + _headerBuf.toString());\r
204                                         Log.e("HandleReceivedBytes", "_currentHeader: " + _currentHeader.toString());\r
205                                         Log.e("HandleReceivedBytes", "receivedBytes: " + receivedBytes.toString());\r
206                                         Log.e("HandleReceivedBytes", "receivedBytesReadPos: " + receivedBytesReadPos);\r
207                                         Log.e("HandleReceivedBytes", "_headerBufWritePos: " + _headerBufWritePos);\r
208                                         Log.e("HandleReceivedBytes", "headerBytesNeeded: " + headerBytesNeeded);\r
209                                         handleProtocolError("Error handling protocol message from SMARTDEVICELINK, header invalid.", \r
210                                                         new SmartDeviceLinkException("Error handling protocol message from SMARTDEVICELINK, header invalid.", SmartDeviceLinkExceptionCause.INVALID_HEADER));\r
211                                         return;                                 \r
212                                 }\r
213                                 _dataBufWritePos = 0;\r
214                         }\r
215                 }\r
216 \r
217                 int bytesLeft = receivedBytesLength - receivedBytesReadPos;\r
218                 int bytesNeeded = _dataBuf.length - _dataBufWritePos;\r
219                 // If I don't have enough bytes for the message, just grab what's there.\r
220                 if (bytesLeft < bytesNeeded) {\r
221                         System.arraycopy(receivedBytes, receivedBytesReadPos, _dataBuf,\r
222                                         _dataBufWritePos, bytesLeft);\r
223                         _dataBufWritePos += bytesLeft;\r
224                         return;\r
225                 } else {\r
226                 // Fill the buffer and call the handler!\r
227                         System.arraycopy(receivedBytes, receivedBytesReadPos, _dataBuf, _dataBufWritePos, bytesNeeded);\r
228                         receivedBytesReadPos += bytesNeeded;\r
229 \r
230                         MessageFrameAssembler assembler = getFrameAssemblerForFrame(_currentHeader);\r
231                         handleProtocolFrameReceived(_currentHeader, _dataBuf, assembler);\r
232 \r
233                         // Reset all class member variables for next frame\r
234                         _dataBuf = null;\r
235                         _dataBufWritePos = 0;\r
236                         _haveHeader = false;\r
237                         _headerBuf = new byte[HEADER_SIZE];\r
238                         _currentHeader = null;\r
239                         _headerBufWritePos = 0;\r
240                         \r
241                         // If there are any bytes left, recurse.\r
242                         int moreBytesLeft = receivedBytesLength - receivedBytesReadPos;\r
243                         if (moreBytesLeft > 0) {\r
244                                 byte[] moreBytes = new byte[moreBytesLeft];\r
245                                 System.arraycopy(receivedBytes, receivedBytesReadPos,\r
246                                                 moreBytes, 0, moreBytesLeft);\r
247                                 HandleReceivedBytes(moreBytes, moreBytesLeft);\r
248                         }\r
249                 }\r
250         }\r
251         \r
252         protected MessageFrameAssembler getFrameAssemblerForFrame(ProtocolFrameHeader header) {\r
253                 Hashtable<Integer, MessageFrameAssembler> hashSessionID = _assemblerForSessionID.get(new Byte(header.getSessionID()));\r
254                 if (hashSessionID == null) {\r
255                         hashSessionID = new Hashtable<Integer, MessageFrameAssembler>();\r
256                         _assemblerForSessionID.put(new Byte(header.getSessionID()), hashSessionID);\r
257                 } // end-if\r
258                 \r
259                 MessageFrameAssembler ret = (MessageFrameAssembler) _assemblerForMessageID.get(new Integer(header.getMessageID()));\r
260                 if (ret == null) {\r
261                         ret = new MessageFrameAssembler();\r
262                         _assemblerForMessageID.put(new Integer(header.getMessageID()), ret);\r
263                 } // end-if\r
264                 \r
265                 return ret;\r
266         } // end-method\r
267 \r
268         protected class MessageFrameAssembler {\r
269                 protected boolean hasFirstFrame = false;\r
270                 protected boolean hasSecondFrame = false;\r
271                 protected ByteArrayOutputStream accumulator = null;\r
272                 protected int totalSize = 0;\r
273                 protected int framesRemaining = 0;\r
274 \r
275                 protected void handleFirstDataFrame(ProtocolFrameHeader header, byte[] data) {\r
276                         //The message is new, so let's figure out how big it is.\r
277                         hasFirstFrame = true;\r
278                         totalSize = BitConverter.intFromByteArray(data, 0) - HEADER_SIZE;\r
279                         framesRemaining = BitConverter.intFromByteArray(data, 4);\r
280                         accumulator = new ByteArrayOutputStream(totalSize);\r
281                 }\r
282                 \r
283                 protected void handleSecondFrame(ProtocolFrameHeader header, byte[] data) {\r
284                         handleRemainingFrame(header, data);\r
285                 }\r
286                 \r
287                 protected void handleRemainingFrame(ProtocolFrameHeader header, byte[] data) {\r
288                         accumulator.write(data, 0, header.getDataSize());\r
289                         notifyIfFinished(header);\r
290                 }\r
291                 \r
292                 protected void notifyIfFinished(ProtocolFrameHeader header) {\r
293                         //if (framesRemaining == 0) {\r
294                         if (header.getFrameType() == FrameType.Consecutive && header.getFrameData() == 0x0) \r
295                         {\r
296                                 ProtocolMessage message = new ProtocolMessage();\r
297                                 message.setSessionType(header.getSessionType());\r
298                                 message.setSessionID(header.getSessionID());\r
299                                 //If it is SmartDeviceLinkPro 2.0 it must have binary header\r
300                                 if (_version == 2) {\r
301                                         BinaryFrameHeader binFrameHeader = BinaryFrameHeader.\r
302                                                         parseBinaryHeader(accumulator.toByteArray());\r
303                                         message.setVersion(_version);\r
304                                         message.setRPCType(binFrameHeader.getRPCType());\r
305                                         message.setFunctionID(binFrameHeader.getFunctionID());\r
306                                         message.setCorrID(binFrameHeader.getCorrID());\r
307                                         if (binFrameHeader.getJsonSize() > 0) message.setData(binFrameHeader.getJsonData());\r
308                                         if (binFrameHeader.getBulkData() != null) message.setBulkData(binFrameHeader.getBulkData());\r
309                                 } else message.setData(accumulator.toByteArray());\r
310                                 \r
311                                 _assemblerForMessageID.remove(header.getMessageID());\r
312                                 \r
313                                 try {\r
314                                         handleProtocolMessageReceived(message);\r
315                                 } catch (Exception excp) {\r
316                                         DebugTool.logError(FailurePropagating_Msg + "onProtocolMessageReceived: " + excp.toString(), excp);\r
317                                 } // end-catch\r
318                                 \r
319                                 hasFirstFrame = false;\r
320                                 hasSecondFrame = false;\r
321                                 accumulator = null;\r
322                         } // end-if\r
323                 } // end-method\r
324                 \r
325                 protected void handleMultiFrameMessageFrame(ProtocolFrameHeader header, byte[] data) {\r
326                         //if (!hasFirstFrame) {\r
327                         //      hasFirstFrame = true;\r
328                         if (header.getFrameType() == FrameType.First)\r
329                         {\r
330                                 handleFirstDataFrame(header, data);\r
331                         }\r
332                                 \r
333                         //} else if (!hasSecondFrame) {\r
334                         //      hasSecondFrame = true;\r
335                         //      framesRemaining--;\r
336                         //      handleSecondFrame(header, data);\r
337                         //} else {\r
338                         //      framesRemaining--;\r
339                         else\r
340                         {\r
341                                 handleRemainingFrame(header, data);\r
342                         }\r
343                                 \r
344                         //}\r
345                 } // end-method\r
346                 \r
347                 protected void handleFrame(ProtocolFrameHeader header, byte[] data) {\r
348                         if (header.getFrameType().equals(FrameType.Control)) {\r
349                                 handleControlFrame(header, data);\r
350                         } else {\r
351                                 // Must be a form of data frame (single, first, consecutive, etc.)\r
352                                 if (   header.getFrameType() == FrameType.First\r
353                                         || header.getFrameType() == FrameType.Consecutive\r
354                                         ) {\r
355                                         handleMultiFrameMessageFrame(header, data);\r
356                                 } else {\r
357                                         handleSingleFrameMessageFrame(header, data);\r
358                                 }\r
359                         } // end-if\r
360                 } // end-method\r
361                 \r
362                 \r
363                 private void handleControlFrame(ProtocolFrameHeader header, byte[] data) {\r
364                         if (header.getFrameData() == FrameDataControlFrameType.StartSession.getValue()) {\r
365                                 sendStartProtocolSessionACK(header.getSessionType(), header.getSessionID());\r
366                         } else if (header.getFrameData() == FrameDataControlFrameType.StartSessionACK.getValue()) {\r
367                                 // Use this sessionID to create a message lock\r
368                                 Object messageLock = _messageLocks.get(header.getSessionID());\r
369                                 if (messageLock == null) {\r
370                                         messageLock = new Object();\r
371                                         _messageLocks.put(header.getSessionID(), messageLock);\r
372                                 }\r
373                                 //hashID = BitConverter.intFromByteArray(data, 0);\r
374                                 if (_version == 2) hashID = header.getMessageID();\r
375                                 handleProtocolSessionStarted(header.getSessionType(), header.getSessionID(), _version, "");                             \r
376                         } else if (header.getFrameData() == FrameDataControlFrameType.StartSessionNACK.getValue()) {\r
377                                 handleProtocolError("Got StartSessionNACK for protocol sessionID=" + header.getSessionID(), null);\r
378                         } else if (header.getFrameData() == FrameDataControlFrameType.EndSession.getValue()) {\r
379                                 //if (hashID == BitConverter.intFromByteArray(data, 0)) \r
380                                 if (_version == 2) {\r
381                                         if (hashID == header.getMessageID())\r
382                                                 handleProtocolSessionEnded(header.getSessionType(), header.getSessionID(), "");\r
383                                 } else handleProtocolSessionEnded(header.getSessionType(), header.getSessionID(), "");\r
384                         }\r
385                 } // end-method\r
386                                 \r
387                 private void handleSingleFrameMessageFrame(ProtocolFrameHeader header, byte[] data) {\r
388                         ProtocolMessage message = new ProtocolMessage();\r
389                         if (header.getSessionType() == SessionType.RPC) {\r
390                                 message.setMessageType(MessageType.RPC);\r
391                         } else if (header.getSessionType() == SessionType.Bulk_Data) {\r
392                                 message.setMessageType(MessageType.BULK);\r
393                         } // end-if\r
394                         message.setSessionType(header.getSessionType());\r
395                         message.setSessionID(header.getSessionID());\r
396                         //If it is SmartDeviceLinkPro 2.0 it must have binary header\r
397                         if (_version == 2) {\r
398                                 BinaryFrameHeader binFrameHeader = BinaryFrameHeader.\r
399                                                 parseBinaryHeader(data);\r
400                                 message.setVersion(_version);\r
401                                 message.setRPCType(binFrameHeader.getRPCType());\r
402                                 message.setFunctionID(binFrameHeader.getFunctionID());\r
403                                 message.setCorrID(binFrameHeader.getCorrID());\r
404                                 if (binFrameHeader.getJsonSize() > 0) message.setData(binFrameHeader.getJsonData());\r
405                                 if (binFrameHeader.getBulkData() != null) message.setBulkData(binFrameHeader.getBulkData());\r
406                         } else message.setData(data);\r
407                         \r
408                         _assemblerForMessageID.remove(header.getMessageID());\r
409                         \r
410                         try {\r
411                                 handleProtocolMessageReceived(message);\r
412                         } catch (Exception ex) {\r
413                                 DebugTool.logError(FailurePropagating_Msg + "onProtocolMessageReceived: " + ex.toString(), ex);\r
414                                 handleProtocolError(FailurePropagating_Msg + "onProtocolMessageReceived: ", ex);\r
415                         } // end-catch\r
416                 } // end-method\r
417         } // end-class\r
418 } // end-class