1 package com.smartdevicelink.transport;
\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
10 import com.smartdevicelink.util.*;
\r
12 public class SiphonServer {
\r
13 // Prohibit use of no-arg ctor
\r
14 private SiphonServer() {}
\r
16 enum SiphonDataType {
\r
17 fromSmartDeviceLink,
\r
25 // Boolean to enable/disable the siphon
\r
26 private static Boolean m_siphonEnabled = false;
\r
28 // Boolean to determine if the siphon has been initialized
\r
29 private static Boolean m_siphonInitialized = false;
\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
37 // Initial timestamp in MS
\r
38 private static long m_startTimeStamp = 0;
\r
40 // SMARTDEVICELINK Trace Message Version
\r
41 private static byte m_smartDeviceLinkTraceMsgVersionNumber = 1;
\r
43 // Max number of ports to attempt a connection on
\r
44 private final static Integer MAX_NUMBER_OF_PORT_ATTEMPTS = 1000;
\r
46 // Starting port for future port attempts
\r
47 private final static short FIRST_PORT_TO_ATTEMPT_CONNECTION = 7474;
\r
49 // Boolean to determine if formatted trace is being sent
\r
50 private static Boolean m_sendingFormattedTrace = false;
\r
52 public static void enableSiphonServer() {
\r
53 m_siphonEnabled = true;
\r
56 public static void disableSiphonServer() {
\r
57 m_siphonEnabled = false;
\r
60 public static boolean init() {
\r
61 // Only initialize if the siphon has not been initialized previously
\r
63 // Check here to be lean. If true, no need to smartDeviceLinkhronize
\r
64 if (m_siphonInitialized) {
\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
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
81 m_siphonInitialized = true;
\r
86 return m_siphonInitialized;
\r
89 public static void closeServer() throws IOException {
\r
91 if (m_siphonClientThread != null) {
\r
92 m_siphonClientThread.halt();
\r
93 m_siphonClientThread = null;
\r
96 if (m_listeningSocket != null) {
\r
97 m_listeningSocket.close();
\r
98 m_listeningSocket = null;
\r
101 if (m_siphonSocket != null) {
\r
102 m_siphonSocket.close();
\r
103 m_siphonSocket = null;
\r
106 if (m_siphonSocketOutputStream != null) {
\r
107 m_siphonSocketOutputStream.close();
\r
108 m_siphonSocketOutputStream = null;
\r
112 public static Boolean sendBytesFromAPP(byte[] msgBytes, int offset, int length) {
\r
114 if (m_sendingFormattedTrace) {
\r
118 return sendSiphonData(SiphonDataType.fromApp, msgBytes, offset, length);
\r
121 public static Boolean sendBytesFromSMARTDEVICELINK(byte[] msgBytes, int offset, int length) {
\r
123 if (m_sendingFormattedTrace) {
\r
127 return sendSiphonData(SiphonDataType.fromSmartDeviceLink, msgBytes, offset, length);
\r
130 public static Boolean sendSiphonLogData(String message) {
\r
132 if (m_sendingFormattedTrace) {
\r
136 if (message == null || message.length() == 0) {
\r
140 byte messageBytes[] = null;
\r
141 int messageLength = 0;
\r
144 messageBytes = message.getBytes("UTF-8");
\r
145 } catch (UnsupportedEncodingException e) {
\r
149 messageLength = messageBytes.length;
\r
150 return sendSiphonData(SiphonDataType.appLog, messageBytes, 0, messageLength);
\r
154 public static Boolean sendFormattedTraceMessage(String message) {
\r
156 if (message == null || message.length() == 0) {
\r
160 byte messageBytes[] = null;
\r
161 int messageLength = 0;
\r
164 messageBytes = message.getBytes("UTF-8");
\r
165 } catch (UnsupportedEncodingException e) {
\r
169 messageLength = messageBytes.length;
\r
170 if (sendSiphonData(SiphonDataType.formattedTrace, messageBytes, 0, messageLength)) {
\r
171 m_sendingFormattedTrace = true;
\r
178 private static Boolean sendSiphonData(SiphonDataType direction, byte[] msgBytes, int offset, int length) {
\r
179 byte siphonDataTypeIndicator = 0x00;
\r
181 long currentDateTime = System.currentTimeMillis();
\r
182 Integer deltaTimeMills = null;
\r
184 deltaTimeMills = (int)(currentDateTime - m_startTimeStamp);
\r
186 switch(direction) {
\r
187 case fromSmartDeviceLink:
\r
188 siphonDataTypeIndicator = 0x00;
\r
191 siphonDataTypeIndicator = 0x01;
\r
194 siphonDataTypeIndicator = 0x02;
\r
196 case formattedTrace:
\r
197 siphonDataTypeIndicator = 0x03;
\r
199 case baselineTimeStamp:
\r
200 siphonDataTypeIndicator = 0x04;
\r
202 case traceSettings:
\r
203 siphonDataTypeIndicator = 0x05;
\r
206 siphonDataTypeIndicator = 0x00;
\r
210 // Set high bit to indicate new format
\r
211 siphonDataTypeIndicator = (byte)((byte)0x80 | siphonDataTypeIndicator);
\r
213 return sendDataToSiphonSocket(siphonDataTypeIndicator, deltaTimeMills, msgBytes, offset, length);
\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
222 if (msgBytes == null || length == 0) {
\r
226 OutputStream siphonOutputStream = null;
\r
228 synchronized (m_siphonLock) {
\r
229 siphonOutputStream = m_siphonSocketOutputStream;
\r
232 if (siphonOutputStream == null) {
\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
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
253 private static class SiphonServerThread extends Thread {
\r
255 private Boolean isHalted = false;
\r
258 public void halt() {
\r
262 private boolean findOpenSocket(short port) {
\r
263 // Accept incoming sihpon connection from trace utility.
\r
264 Boolean foundOpenPort = false;
\r
267 // Listen to accept incoming connection from SMARTDEVICELINK
\r
268 while (!foundOpenPort) {
\r
270 m_listeningSocket = new ServerSocket(listenPort);
\r
271 foundOpenPort = true;
\r
272 } catch (BindException ex) {
\r
274 if(listenPort > port + MAX_NUMBER_OF_PORT_ATTEMPTS) {
\r
277 } catch (IOException e) {
\r
282 return foundOpenPort;
\r
285 private void startServerOnPort() throws IOException {
\r
286 Socket newSocket = null;
\r
288 // Wait for a connection
\r
289 newSocket = m_listeningSocket.accept();
\r
291 // If isHalted after accept() delay, return
\r
296 synchronized (m_siphonLock) {
\r
297 // Reset siphonSocketOutputStream
\r
298 if (m_siphonSocketOutputStream != null) {
\r
300 m_siphonSocketOutputStream.close();
\r
301 } catch (IOException e) {
\r
304 m_siphonSocketOutputStream = null;
\r
307 // Reset siphonSocket
\r
308 if (m_siphonSocket != null) {
\r
310 m_siphonSocket.close();
\r
311 } catch (IOException e) {
\r
314 m_siphonSocket = null;
\r
317 // Store the new socket
\r
318 m_siphonSocket = newSocket;
\r
320 // Set Socket Options
\r
321 m_siphonSocket.setKeepAlive(true);
\r
323 // Get the output stream of the connection
\r
324 m_siphonSocketOutputStream = m_siphonSocket.getOutputStream();
\r
326 // Output version number to the Siphon upon connection (version number prepending to logInfo)
\r
327 DebugTool.logInfo("Siphon connected.");
\r
332 public void run() {
\r
334 if (findOpenSocket(FIRST_PORT_TO_ATTEMPT_CONNECTION)) {
\r
335 while (!isHalted) {
\r
336 startServerOnPort();
\r
339 } catch (Exception ex) {
\r
342 if (m_listeningSocket != null) {
\r
344 m_listeningSocket.close();
\r
345 } catch (IOException e) {
\r
348 m_listeningSocket = null;
\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