1 package com.smartdevicelink.protocol;
\r
3 import java.io.ByteArrayOutputStream;
\r
4 import java.util.Hashtable;
\r
6 import android.util.Log;
\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
13 public class SmartDeviceLinkProtocol extends AbstractProtocol {
\r
15 private final static String FailurePropagating_Msg = "Failure propagating ";
\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
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
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
36 private SmartDeviceLinkProtocol() {
\r
40 public SmartDeviceLinkProtocol(IProtocolListener protocolListener) {
\r
41 super(protocolListener);
\r
44 public byte getVersion() {
\r
45 return this._version;
\r
48 public void setVersion(byte version) {
\r
49 this._version = version;
\r
52 MAX_DATA_SIZE = MTU_SIZE - HEADER_SIZE;
\r
53 _headerBuf = new byte[HEADER_SIZE];
\r
57 public void StartProtocolSession(SessionType sessionType) {
\r
58 ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createStartSession(sessionType, 0x00, _version);
\r
59 sendFrameToTransport(header);
\r
62 private void sendStartProtocolSessionACK(SessionType sessionType, byte sessionID) {
\r
63 ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createStartSessionACK(sessionType, sessionID, 0x00, _version);
\r
64 sendFrameToTransport(header);
\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
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
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
94 data = protocolMsg.getData();
\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
105 synchronized(messageLock) {
\r
106 if (data.length > MAX_DATA_SIZE) {
\r
109 ProtocolFrameHeader firstHeader = ProtocolFrameHeaderFactory.createMultiSendDataFirst(sessionType, sessionID, messageID, _version);
\r
111 // Assemble first frame.
\r
112 int frameCount = data.length / MAX_DATA_SIZE;
\r
113 if (data.length % MAX_DATA_SIZE > 0) {
\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
123 handleProtocolFrameToSend(firstHeader, firstFrameData, 0, firstFrameData.length);
\r
125 int currentOffset = 0;
\r
126 byte frameSequenceNumber = 0;
\r
128 for (int i = 0; i < frameCount; i++) {
\r
129 if (i < (frameCount - 1)) {
\r
130 frameSequenceNumber = (byte)(i + 1);
\r
132 frameSequenceNumber = ProtocolFrameHeader.FrameDataFinalConsecutiveFrame;
\r
135 int bytesToWrite = data.length - currentOffset;
\r
136 if (bytesToWrite > MAX_DATA_SIZE) {
\r
137 bytesToWrite = MAX_DATA_SIZE;
\r
140 ProtocolFrameHeader consecHeader = ProtocolFrameHeaderFactory.createMultiSendDataRest(sessionType, sessionID, bytesToWrite, frameSequenceNumber , messageID, _version);
\r
141 handleProtocolFrameToSend(consecHeader, data, currentOffset, bytesToWrite);
\r
142 currentOffset += bytesToWrite;
\r
146 ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createSingleSendData(sessionType, sessionID, data.length, messageID, _version);
\r
147 handleProtocolFrameToSend(header, data, 0, data.length);
\r
152 private void sendFrameToTransport(ProtocolFrameHeader header) {
\r
153 handleProtocolFrameToSend(header, null, 0, 0);
\r
156 public void HandleReceivedBytes(byte[] receivedBytes, int receivedBytesLength) {
\r
157 int receivedBytesReadPos = 0;
\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
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
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
193 int iDataSize = _currentHeader.getDataSize();
\r
195 if (iDataSize <= 4000)
\r
197 _dataBuf = new byte[iDataSize];
\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
213 _dataBufWritePos = 0;
\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
226 // Fill the buffer and call the handler!
\r
227 System.arraycopy(receivedBytes, receivedBytesReadPos, _dataBuf, _dataBufWritePos, bytesNeeded);
\r
228 receivedBytesReadPos += bytesNeeded;
\r
230 MessageFrameAssembler assembler = getFrameAssemblerForFrame(_currentHeader);
\r
231 handleProtocolFrameReceived(_currentHeader, _dataBuf, assembler);
\r
233 // Reset all class member variables for next frame
\r
235 _dataBufWritePos = 0;
\r
236 _haveHeader = false;
\r
237 _headerBuf = new byte[HEADER_SIZE];
\r
238 _currentHeader = null;
\r
239 _headerBufWritePos = 0;
\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
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
259 MessageFrameAssembler ret = (MessageFrameAssembler) _assemblerForMessageID.get(new Integer(header.getMessageID()));
\r
261 ret = new MessageFrameAssembler();
\r
262 _assemblerForMessageID.put(new Integer(header.getMessageID()), ret);
\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
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
283 protected void handleSecondFrame(ProtocolFrameHeader header, byte[] data) {
\r
284 handleRemainingFrame(header, data);
\r
287 protected void handleRemainingFrame(ProtocolFrameHeader header, byte[] data) {
\r
288 accumulator.write(data, 0, header.getDataSize());
\r
289 notifyIfFinished(header);
\r
292 protected void notifyIfFinished(ProtocolFrameHeader header) {
\r
293 //if (framesRemaining == 0) {
\r
294 if (header.getFrameType() == FrameType.Consecutive && header.getFrameData() == 0x0)
\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
311 _assemblerForMessageID.remove(header.getMessageID());
\r
314 handleProtocolMessageReceived(message);
\r
315 } catch (Exception excp) {
\r
316 DebugTool.logError(FailurePropagating_Msg + "onProtocolMessageReceived: " + excp.toString(), excp);
\r
319 hasFirstFrame = false;
\r
320 hasSecondFrame = false;
\r
321 accumulator = null;
\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
330 handleFirstDataFrame(header, data);
\r
333 //} else if (!hasSecondFrame) {
\r
334 // hasSecondFrame = true;
\r
335 // framesRemaining--;
\r
336 // handleSecondFrame(header, data);
\r
338 // framesRemaining--;
\r
341 handleRemainingFrame(header, data);
\r
347 protected void handleFrame(ProtocolFrameHeader header, byte[] data) {
\r
348 if (header.getFrameType().equals(FrameType.Control)) {
\r
349 handleControlFrame(header, data);
\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
355 handleMultiFrameMessageFrame(header, data);
\r
357 handleSingleFrameMessageFrame(header, data);
\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
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
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
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
408 _assemblerForMessageID.remove(header.getMessageID());
\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