SDL_Android/SmartDeviceLinkAndroidProxy - added the correct version of the proxy
[profile/ivi/smartdevicelink.git] / SDL_Android / SmartDeviceLinkProxyAndroid / src / com / smartdevicelink / transport / SiphonServer.java
1 package com.smartdevicelink.transport;\r
2 \r
3 import java.io.IOException;\r
4 import java.io.OutputStream;\r
5 import java.io.UnsupportedEncodingException;\r
6 import java.net.BindException;\r
7 import java.net.ServerSocket;\r
8 import java.net.Socket;\r
9 \r
10 import com.smartdevicelink.util.*;\r
11 \r
12 public class SiphonServer {\r
13         // Prohibit use of no-arg ctor\r
14         private SiphonServer() {}\r
15         \r
16         enum SiphonDataType {\r
17                 fromSmartDeviceLink,                            \r
18                 fromApp,                                \r
19                 appLog,\r
20                 formattedTrace,\r
21                 baselineTimeStamp,              \r
22                 traceSettings                   \r
23         }\r
24         \r
25         // Boolean to enable/disable the siphon\r
26         private static Boolean m_siphonEnabled = false;\r
27         \r
28         // Boolean to determine if the siphon has been initialized\r
29         private static Boolean m_siphonInitialized = false;\r
30         \r
31         private static Socket m_siphonSocket = null;\r
32         private static Object m_siphonLock = new Object();\r
33         private static ServerSocket m_listeningSocket = null;\r
34         private static OutputStream m_siphonSocketOutputStream = null;\r
35         private static SiphonServerThread m_siphonClientThread = null;\r
36         \r
37         // Initial timestamp in MS\r
38         private static long m_startTimeStamp = 0;\r
39         \r
40         // SMARTDEVICELINK Trace Message Version\r
41         private static byte m_smartDeviceLinkTraceMsgVersionNumber = 1;\r
42         \r
43         // Max number of ports to attempt a connection on\r
44         private final static Integer MAX_NUMBER_OF_PORT_ATTEMPTS = 1000;\r
45         \r
46         // Starting port for future port attempts\r
47         private final static short FIRST_PORT_TO_ATTEMPT_CONNECTION = 7474;\r
48 \r
49         // Boolean to determine if formatted trace is being sent\r
50         private static Boolean m_sendingFormattedTrace = false;\r
51         \r
52         public static void enableSiphonServer() {\r
53                 m_siphonEnabled = true;\r
54         }\r
55         \r
56         public static void disableSiphonServer() {\r
57                 m_siphonEnabled = false;\r
58         }\r
59 \r
60         public static boolean init() {          \r
61                 // Only initialize if the siphon has not been initialized previously\r
62 \r
63                 // Check here to be lean. If true, no need to smartDeviceLinkhronize\r
64                 if (m_siphonInitialized) {\r
65                         return true;\r
66                 }\r
67                 \r
68                 synchronized (m_siphonLock) {\r
69                         // To prevent a race condition, re-check m_siphonInitialized inside of smartDeviceLinkhronize block\r
70                         if (!m_siphonInitialized) {\r
71                                 if (m_siphonClientThread == null) {\r
72                                         // Set current time stamp \r
73                                         m_startTimeStamp = System.currentTimeMillis();\r
74                                         \r
75                                         // Start Siphon Thread\r
76                             m_siphonClientThread = new SiphonServerThread();\r
77                                 m_siphonClientThread.setName("Siphon");\r
78                                 m_siphonClientThread.setDaemon(true);\r
79                                 m_siphonClientThread.start();\r
80                                 \r
81                                 m_siphonInitialized = true;\r
82                                 } // end-if\r
83                         } // end-lock\r
84                 }\r
85                 \r
86                 return m_siphonInitialized;\r
87         } // end-method\r
88 \r
89         public static void closeServer() throws IOException {\r
90                 \r
91                 if (m_siphonClientThread != null) {\r
92                         m_siphonClientThread.halt();\r
93                         m_siphonClientThread = null;\r
94                 }\r
95                 \r
96                 if (m_listeningSocket != null) {\r
97                         m_listeningSocket.close();\r
98                         m_listeningSocket = null;\r
99                 }\r
100                 \r
101                 if (m_siphonSocket != null) {\r
102                         m_siphonSocket.close();\r
103                         m_siphonSocket = null;\r
104                 }\r
105                 \r
106                 if (m_siphonSocketOutputStream != null) {\r
107                         m_siphonSocketOutputStream.close();\r
108                         m_siphonSocketOutputStream = null;\r
109                 }\r
110         }\r
111     \r
112         public static Boolean sendBytesFromAPP(byte[] msgBytes, int offset, int length) {\r
113                 \r
114                 if (m_sendingFormattedTrace) {\r
115                         return false;\r
116                 }\r
117                 \r
118                 return sendSiphonData(SiphonDataType.fromApp, msgBytes, offset, length);\r
119         } // end-method\r
120 \r
121         public static Boolean sendBytesFromSMARTDEVICELINK(byte[] msgBytes, int offset, int length) {           \r
122 \r
123                 if (m_sendingFormattedTrace) {\r
124                         return false;\r
125                 }\r
126                 \r
127                 return sendSiphonData(SiphonDataType.fromSmartDeviceLink, msgBytes, offset, length);\r
128         } // end-method\r
129         \r
130         public static Boolean sendSiphonLogData(String message) {\r
131                 \r
132                 if (m_sendingFormattedTrace) {\r
133                         return false;\r
134                 }\r
135                 \r
136                 if (message == null || message.length() == 0) {\r
137                         return false;\r
138                 }\r
139                                 \r
140                 byte messageBytes[] = null;\r
141                 int messageLength = 0;\r
142                 \r
143                 try {\r
144                         messageBytes = message.getBytes("UTF-8");\r
145                 } catch (UnsupportedEncodingException e) {\r
146                         return false;\r
147                 }\r
148                 \r
149                 messageLength = messageBytes.length;\r
150                 return sendSiphonData(SiphonDataType.appLog, messageBytes, 0, messageLength);\r
151                 \r
152         }\r
153         \r
154         public static Boolean sendFormattedTraceMessage(String message) {\r
155                                 \r
156                 if (message == null || message.length() == 0) {\r
157                         return false;\r
158                 }\r
159                 \r
160                 byte messageBytes[] = null;\r
161                 int messageLength = 0;\r
162                                 \r
163                 try {\r
164                         messageBytes = message.getBytes("UTF-8");\r
165                 } catch (UnsupportedEncodingException e) {\r
166                         return false;\r
167                 }\r
168                 \r
169                 messageLength = messageBytes.length;\r
170                 if (sendSiphonData(SiphonDataType.formattedTrace, messageBytes, 0, messageLength)) {\r
171                         m_sendingFormattedTrace = true;\r
172                         return true;\r
173                 } else {\r
174                         return false;\r
175                 }\r
176         }\r
177         \r
178         private static Boolean sendSiphonData(SiphonDataType direction, byte[] msgBytes, int offset, int length) {\r
179                 byte siphonDataTypeIndicator = 0x00;\r
180                 \r
181                 long currentDateTime = System.currentTimeMillis();\r
182                 Integer deltaTimeMills = null;\r
183                         \r
184                 deltaTimeMills = (int)(currentDateTime - m_startTimeStamp);\r
185                 \r
186                 switch(direction) {\r
187                         case fromSmartDeviceLink:\r
188                                 siphonDataTypeIndicator = 0x00;\r
189                                 break;\r
190                         case fromApp:\r
191                                 siphonDataTypeIndicator = 0x01;\r
192                                 break;\r
193                         case appLog:\r
194                                 siphonDataTypeIndicator = 0x02;\r
195                                 break;\r
196                         case formattedTrace:\r
197                                 siphonDataTypeIndicator = 0x03;\r
198                                 break;\r
199                         case baselineTimeStamp:\r
200                                 siphonDataTypeIndicator = 0x04;\r
201                                 break;\r
202                         case traceSettings:\r
203                                 siphonDataTypeIndicator = 0x05;\r
204                                 break;\r
205                         default:\r
206                                 siphonDataTypeIndicator = 0x00;\r
207                                 break;\r
208                 }\r
209                 \r
210                 // Set high bit to indicate new format\r
211                 siphonDataTypeIndicator = (byte)((byte)0x80 | siphonDataTypeIndicator);\r
212                 \r
213                 return sendDataToSiphonSocket(siphonDataTypeIndicator, deltaTimeMills, msgBytes, offset, length);\r
214         }\r
215         \r
216         private synchronized static Boolean sendDataToSiphonSocket(byte directionIndicator, Integer timeStamp, \r
217                         byte[] msgBytes, int offset, int length) {\r
218                 if (!m_siphonEnabled) {\r
219                         return false;\r
220                 }\r
221                 \r
222                 if (msgBytes == null || length == 0) {\r
223                         return false;\r
224                 }\r
225                 \r
226                 OutputStream siphonOutputStream = null;\r
227 \r
228                 synchronized (m_siphonLock) {\r
229                         siphonOutputStream = m_siphonSocketOutputStream;\r
230                 } // end-lock\r
231 \r
232                 if (siphonOutputStream == null) {\r
233                         return false;\r
234                 }               \r
235                 \r
236                 try     {               \r
237                         // blobSize = length(of message) + 1(size of direction indicator)\r
238                         //                              + 1 (size of msgVersionNumber) + 4 (size of timeStamp)\r
239                         int blobSize = length + 1 + 1 + 4;\r
240                         \r
241                         siphonOutputStream.write(BitConverter.intToByteArray(blobSize));\r
242                         siphonOutputStream.write(new byte[] {directionIndicator});\r
243                         siphonOutputStream.write(new byte[] {m_smartDeviceLinkTraceMsgVersionNumber});\r
244                         siphonOutputStream.write(intToByteArray(timeStamp));\r
245                         siphonOutputStream.write(msgBytes, offset, length);\r
246                 } catch (Exception ex) {\r
247                         return false;\r
248                 } // end-catch\r
249                 \r
250                 return true;\r
251         } // end-method\r
252     \r
253     private static class SiphonServerThread extends Thread {\r
254         \r
255         private Boolean isHalted = false;\r
256         short listenPort;\r
257                 \r
258                 public void halt() {\r
259                         isHalted = true;\r
260                 }\r
261         \r
262         private boolean findOpenSocket(short port) {\r
263                 // Accept incoming sihpon connection from trace utility.\r
264                 Boolean foundOpenPort = false;\r
265                 listenPort = port;\r
266                 \r
267                 // Listen to accept incoming connection from SMARTDEVICELINK\r
268                 while (!foundOpenPort) {\r
269                         try {\r
270                                 m_listeningSocket = new ServerSocket(listenPort);\r
271                                 foundOpenPort = true;\r
272                         } catch (BindException ex) {\r
273                                 listenPort++;\r
274                                 if(listenPort > port + MAX_NUMBER_OF_PORT_ATTEMPTS) {\r
275                                         return false;\r
276                                 }\r
277                         } catch (IOException e) {\r
278                                 return false;\r
279                                 }\r
280                 }\r
281                 \r
282                 return foundOpenPort;\r
283         }\r
284         \r
285         private void startServerOnPort() throws IOException {\r
286                 Socket newSocket = null;\r
287                 \r
288                 // Wait for a connection\r
289                 newSocket = m_listeningSocket.accept(); \r
290             \r
291             // If isHalted after accept() delay, return\r
292                 if (isHalted) {\r
293                         return;\r
294                 }\r
295 \r
296                 synchronized (m_siphonLock) {\r
297                         // Reset siphonSocketOutputStream\r
298                         if (m_siphonSocketOutputStream != null) {\r
299                                 try {\r
300                                         m_siphonSocketOutputStream.close();\r
301                                 } catch (IOException e) {\r
302                                         // Do nothing\r
303                                 }\r
304                                 m_siphonSocketOutputStream = null;\r
305                         }\r
306 \r
307                         // Reset siphonSocket\r
308                         if (m_siphonSocket != null) {\r
309                                 try {\r
310                                         m_siphonSocket.close();\r
311                                 } catch (IOException e) {\r
312                                         // Do nothing\r
313                                 }\r
314                                 m_siphonSocket = null;\r
315                         }\r
316 \r
317                         // Store the new socket\r
318                         m_siphonSocket = newSocket;\r
319                         \r
320                         // Set Socket Options\r
321                                 m_siphonSocket.setKeepAlive(true);\r
322                                 \r
323                                 // Get the output stream of the connection\r
324                                 m_siphonSocketOutputStream = m_siphonSocket.getOutputStream();\r
325                                 \r
326                         // Output version number to the Siphon upon connection (version number prepending to logInfo)\r
327                                 DebugTool.logInfo("Siphon connected.");\r
328                 } // end-lock\r
329         } // end-method\r
330         \r
331         @Override\r
332         public void run() {\r
333                 try     {\r
334                         if (findOpenSocket(FIRST_PORT_TO_ATTEMPT_CONNECTION)) {\r
335                                 while (!isHalted) {\r
336                                         startServerOnPort();\r
337                                 }\r
338                         } \r
339                 } catch (Exception ex) {\r
340                         // Do nothing\r
341                 } finally {\r
342                                 if (m_listeningSocket != null) {\r
343                                         try {\r
344                                                 m_listeningSocket.close();\r
345                                         } catch (IOException e) {\r
346                                                 // Do nothing\r
347                                         }\r
348                                         m_listeningSocket = null;\r
349                                 }\r
350                         }\r
351         }\r
352     }\r
353     \r
354     private static final byte[] intToByteArray(int value) {\r
355         return new byte[] {\r
356                 (byte)(value >>> 24),\r
357                 (byte)(value >>> 16),\r
358                 (byte)(value >>> 8),\r
359                 (byte)value};\r
360     }\r
361 } // end-class