summaryrefslogtreecommitdiff
path: root/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java
blob: b6eac12e024c39829fa36789a5200ddac714d759 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/*
 * Copyright (c) 2019, Livio, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of the Livio Inc. nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package com.smartdevicelink.transport;

import com.smartdevicelink.protocol.SdlPacket;

import static com.smartdevicelink.protocol.SdlProtocol.V1_HEADER_SIZE;
import static com.smartdevicelink.protocol.SdlProtocol.V1_V2_MTU_SIZE;


public class SdlPsm{
	//private static final String TAG = "Sdl PSM";
	//Each state represents the byte that should be incoming
	
	public static final int START_STATE							= 	0x0;
	public static final int SERVICE_TYPE_STATE					= 	0x02;
	public static final int CONTROL_FRAME_INFO_STATE			= 	0x03;
	public static final int SESSION_ID_STATE					= 	0x04;
	public static final int DATA_SIZE_1_STATE					= 	0x05;
	public static final int DATA_SIZE_2_STATE					= 	0x06;
	public static final int DATA_SIZE_3_STATE					= 	0x07;
	public static final int DATA_SIZE_4_STATE					= 	0x08;
	public static final int MESSAGE_1_STATE						= 	0x09;
	public static final int MESSAGE_2_STATE						= 	0x0A;
	public static final int MESSAGE_3_STATE						= 	0x0B;
	public static final int MESSAGE_4_STATE						= 	0x0C;
	public static final int DATA_PUMP_STATE						= 	0x0D;
	public static final int FINISHED_STATE 						=	0xFF;
	public static final int ERROR_STATE 						= 	-1;

	
	private static final byte FIRST_FRAME_DATA_SIZE 			= 0x08;
	
	private static final int VERSION_MASK 						= 0xF0; //4 highest bits
	private static final int COMPRESSION_MASK 					= 0x08; //4th lowest bit
	private static final int FRAME_TYPE_MASK 					= 0x07; //3 lowest bits
	
	
	
	int state ;
	
	int version;
	boolean compression;   	
	int frameType;
	int serviceType;		
	int controlFrameInfo;	
	int sessionId;			
	int dumpSize, dataLength;
	int messageId = 0;
	
	byte[] payload;
	
	public SdlPsm(){
		reset();
	}
	
	public boolean handleByte(byte data) {
		//Log.trace(TAG, data + " = incoming");
		state = transitionOnInput(data,state);

		return state != ERROR_STATE;
	}
	
	private int transitionOnInput(byte rawByte, int state){
		switch(state){
		case START_STATE:
			version = (rawByte&(byte)VERSION_MASK)>>4;
			//Log.trace(TAG, "Version: " + version);
			if(version==0){ //It should never be 0
				return ERROR_STATE;
			}
			compression = (1 == ((rawByte&(byte)COMPRESSION_MASK)>>3));
			
			
			frameType = rawByte&(byte)FRAME_TYPE_MASK;
			//Log.trace(TAG, rawByte + " = Frame Type: " + frameType);
			
			if((version < 1 || version > 5) //These are known versions supported by this library.
					&& frameType!=SdlPacket.FRAME_TYPE_CONTROL){
					return ERROR_STATE;
			}

			if(frameType<SdlPacket.FRAME_TYPE_CONTROL || frameType > SdlPacket.FRAME_TYPE_CONSECUTIVE){
				return ERROR_STATE;
			}
			
			return SERVICE_TYPE_STATE;
			
		case SERVICE_TYPE_STATE:
			serviceType = (int)(rawByte&0xFF);
			return CONTROL_FRAME_INFO_STATE;
			
		case CONTROL_FRAME_INFO_STATE:
			controlFrameInfo = (int)(rawByte&0xFF);  
			//Log.trace(TAG,"Frame Info: " + controlFrameInfo);
			switch(frameType){
				case SdlPacket.FRAME_TYPE_CONTROL:
					/*if(frameInfo<FRAME_INFO_HEART_BEAT 
							|| (frameInfo>FRAME_INFO_END_SERVICE_ACK 
									&& (frameInfo!=FRAME_INFO_SERVICE_DATA_ACK || frameInfo!=FRAME_INFO_HEART_BEAT_ACK))){
						return ERROR_STATE;
					}*/ //Although some bits are reserved...whatever
					break;
				case SdlPacket.FRAME_TYPE_SINGLE: //Fall through since they are both the same
				case SdlPacket.FRAME_TYPE_FIRST:
					if(controlFrameInfo!=0x00){
						return ERROR_STATE;
					}
					break;
				case SdlPacket.FRAME_TYPE_CONSECUTIVE:
					//It might be a good idea to check packet sequence numbers here
					break;

				default:
					return ERROR_STATE;
			}
			return SESSION_ID_STATE;
			
		case SESSION_ID_STATE:
			sessionId = (int)(rawByte&0xFF);
			return DATA_SIZE_1_STATE;
			
		case DATA_SIZE_1_STATE:
			//First data size byte
			//Log.d(TAG, "Data byte 1: " + rawByte);
			dataLength += ((int)(rawByte& 0xFF))<<24; //3 bytes x 8 bits
			//Log.d(TAG, "Data Size 1 : " + dataLength);
			return DATA_SIZE_2_STATE;
			
		case DATA_SIZE_2_STATE:
			//Log.d(TAG, "Data byte 2: " + rawByte);
			dataLength += ((int)(rawByte& 0xFF))<<16; //2 bytes x 8 bits
			//Log.d(TAG, "Data Size 2 : " + dataLength);
			return DATA_SIZE_3_STATE;
			
		case DATA_SIZE_3_STATE:
			//Log.d(TAG, "Data byte 3: " + rawByte);
			dataLength += ((int)(rawByte& 0xFF))<<8; //1 byte x 8 bits
			//Log.d(TAG, "Data Size 3 : " + dataLength);
			return DATA_SIZE_4_STATE;
			
		case DATA_SIZE_4_STATE:
			//Log.d(TAG, "Data byte 4: " + rawByte);
			dataLength+=((int)rawByte) & 0xFF;
			//Log.trace(TAG, "Data Size: " + dataLength);
			//We should have data length now for the pump state
			switch(frameType){ //If all is correct we should break out of this switch statement
			case SdlPacket.FRAME_TYPE_SINGLE:
			case SdlPacket.FRAME_TYPE_CONSECUTIVE:
				break;
			case SdlPacket.FRAME_TYPE_CONTROL:
				//Ok, well here's some interesting bit of knowledge. Because the start session request is from the phone with no knowledge of version it sends out
				//a v1 packet. THEREFORE there is no message id field. **** Now you know and knowing is half the battle ****
				if(version==1 && controlFrameInfo == SdlPacket.FRAME_INFO_START_SERVICE){
					if(dataLength==0){
						return FINISHED_STATE; //We are done if we don't have any payload
					}
					if(dataLength <= V1_V2_MTU_SIZE - V1_HEADER_SIZE){ // sizes from protocol/WiProProtocol.java
						payload = new byte[dataLength];
					}else{
						return ERROR_STATE;
					}
					dumpSize = dataLength;
					return DATA_PUMP_STATE;
				}
				break; 
				
			case SdlPacket.FRAME_TYPE_FIRST:
				if(dataLength==FIRST_FRAME_DATA_SIZE){
					break;
				}
			default:
				return ERROR_STATE;
			}
			if(version==1){ //Version 1 packets will not have message id's
				if(dataLength == 0){
					return FINISHED_STATE; //We are done if we don't have any payload
				}
				if(dataLength <= V1_V2_MTU_SIZE - V1_HEADER_SIZE){ // sizes from protocol/WiProProtocol.java
					payload = new byte[dataLength];
				}else{
					return ERROR_STATE;
				}
				dumpSize = dataLength;
				return DATA_PUMP_STATE;
			}else{
				return MESSAGE_1_STATE;
			}
			
		case MESSAGE_1_STATE:
			messageId += ((int)(rawByte& 0xFF))<<24; //3 bytes x 8 bits
			return MESSAGE_2_STATE;
			
		case MESSAGE_2_STATE:
			messageId += ((int)(rawByte& 0xFF))<<16; //2 bytes x 8 bits
			return MESSAGE_3_STATE;
			
		case MESSAGE_3_STATE:
			messageId += ((int)(rawByte& 0xFF))<<8; //1 byte x 8 bits
			return MESSAGE_4_STATE;
			
		case MESSAGE_4_STATE:
			messageId+=((int)rawByte) & 0xFF;

			if(dataLength==0){
				return FINISHED_STATE; //We are done if we don't have any payload
			}
			try{
				payload = new byte[dataLength];
			}catch(OutOfMemoryError oom){
				return ERROR_STATE;
			}
			dumpSize = dataLength;
			return DATA_PUMP_STATE;
			
		case DATA_PUMP_STATE:
			payload[dataLength-dumpSize] = rawByte;
			dumpSize--;
			//Do we have any more bytes to read in?
			if(dumpSize>0){
				return DATA_PUMP_STATE;
			}
			else if(dumpSize==0){
				return FINISHED_STATE;
			}else{
				return ERROR_STATE;
			}
		case FINISHED_STATE: //We shouldn't be here...Should have been reset
		default: 
			return ERROR_STATE;
			
		}
		
	}
	
	public SdlPacket getFormedPacket(){
		if(state==FINISHED_STATE){
			//Log.trace(TAG, "Finished packet.");
			return new SdlPacket(version, compression, frameType,
					serviceType, controlFrameInfo, sessionId,
					dataLength, messageId, payload);
		}else{
			return null;
		}
	}
	
	public int getState() {
		return state;
	}

	public void reset() {
		version = 0;
		state = START_STATE;
		messageId = 0;
		dataLength = 0;
		frameType = 0x00; //Set it to null
		payload = null;
	}

}