Added Mike's tester application (not ideal but easy to update)
[profile/ivi/smartdevicelink.git] / SDL_Android / SmartDeviceLinkProxyAndroid / src / com / smartdevicelink / trace / SyncTrace.java
1 //
2 // Copyright (c) 2013 Ford Motor Company
3 //
4 package com.smartdevicelink.trace;
5
6 import java.sql.Timestamp;
7
8 import android.bluetooth.BluetoothDevice;
9 import android.os.Build;
10 import android.os.Debug;
11 import android.os.Process;
12
13 import com.smartdevicelink.protocol.ProtocolFrameHeader;
14 import com.smartdevicelink.protocol.enums.FrameDataControlFrameType;
15 import com.smartdevicelink.protocol.enums.FrameType;
16 import com.smartdevicelink.protocol.enums.SessionType;
17 import com.smartdevicelink.proxy.RPCMessage;
18 import com.smartdevicelink.proxy.RPCRequest;
19 import com.smartdevicelink.proxy.RPCResponse;
20 import com.smartdevicelink.trace.enums.DetailLevel;
21 import com.smartdevicelink.trace.enums.InterfaceActivityDirection;
22 import com.smartdevicelink.trace.enums.Mod;
23 import com.smartdevicelink.transport.SiphonServer;
24 import com.smartdevicelink.util.BitConverter;
25 import com.smartdevicelink.util.DebugTool;
26 import com.smartdevicelink.util.NativeLogTool;
27
28 /* This class handles the global TraceSettings as requested by the users either through the combination of the following
29    1. System defaults
30    2. Application XML config
31    3. Programmatic requests from application itself
32
33    It is manifested in the <SmartDeviceLink>...</SmartDeviceLink> tags
34  */
35
36 public class SyncTrace {
37         private static final String SmartDeviceLink_LIB_TRACE_KEY = "42baba60-eb57-11df-98cf-0800200c9a66";
38         
39         static boolean canWriteLogs = false;
40                 
41         public static final String SYSTEM_LOG_TAG = "SyncTrace";
42         
43         private static long baseTics  = java.lang.System.currentTimeMillis();
44         private final static String KeyStr = SmartDeviceLink_LIB_TRACE_KEY;
45         private static boolean acceptAPITraceAdjustments = true;
46
47         protected static ISTListener m_appTraceListener = null;
48
49         ///
50         ///  The PUBLIC interface to SyncTrace starts here
51         ///
52
53
54         public static void setAcceptAPITraceAdjustments(Boolean APITraceAdjustmentsAccepted) {
55                 if (APITraceAdjustmentsAccepted != null) {
56                         acceptAPITraceAdjustments = APITraceAdjustmentsAccepted;
57                 }
58         }
59         
60         public static boolean getAcceptAPITraceAdjustments() {
61                 return acceptAPITraceAdjustments;
62         }
63         
64         public static void setAppTraceListener(ISTListener listener) {
65                 m_appTraceListener = listener;
66         } // end-method
67
68         public static void setTracingEnable(Boolean enable) {
69                 if (enable != null) {
70                         canWriteLogs = enable;
71                 }
72         } // end-method
73
74         public static void setAppTraceLevel(DetailLevel dt) {
75                 if ( dt != null && acceptAPITraceAdjustments)
76                         DiagLevel.setLevel(Mod.app, dt);
77         } // end-method
78
79         public static void setProxyTraceLevel(DetailLevel dt) {
80                 if (dt != null && acceptAPITraceAdjustments)
81                         DiagLevel.setLevel(Mod.proxy, dt);
82         } // end-method
83
84         public static void setRpcTraceLevel(DetailLevel dt) {
85                 if (dt != null && acceptAPITraceAdjustments)
86                         DiagLevel.setLevel(Mod.rpc, dt);
87         } // end-method
88
89         public static void setMarshallingTraceLevel(DetailLevel dt) {
90                 if (dt != null && acceptAPITraceAdjustments)
91                         DiagLevel.setLevel(Mod.mar, dt);
92         } // end-method
93
94         public static void setProtocolTraceLevel(DetailLevel dt) {
95                 if (dt != null && acceptAPITraceAdjustments)
96                         DiagLevel.setLevel(Mod.proto, dt);
97         } // end-method
98
99         public static void setTransportTraceLevel(DetailLevel dt) {
100                 if (dt != null && acceptAPITraceAdjustments)
101                                 DiagLevel.setLevel(Mod.tran, dt);
102         } // end-method
103
104         private static String encodeTraceMessage(long timestamp, Mod module, InterfaceActivityDirection msgDirection, String msgBodyXml) {
105                 StringBuilder sb = new StringBuilder("<msg><dms>");
106                 sb.append(timestamp);
107                 sb.append("</dms><pid>");
108                 sb.append(Process.myPid());
109                 sb.append("</pid><tid>");
110                 sb.append(Thread.currentThread().getId());
111                 sb.append("</tid><mod>");
112                 sb.append(module.toString());
113                 sb.append("</mod>");
114                 if (msgDirection != InterfaceActivityDirection.None) {
115                         sb.append("<dir>");
116                         sb.append(interfaceActivityDirectionToString(msgDirection));
117                         sb.append("</dir>");
118                 } // end-if
119                 sb.append(msgBodyXml);
120                 sb.append("</msg>");
121
122                 return sb.toString();
123         } // end-method
124
125         private static String interfaceActivityDirectionToString(InterfaceActivityDirection iaDirection) {
126                 String str = "";
127                 switch (iaDirection) {
128                         case Receive:
129                                 str = "rx";
130                                 break;
131                         case Transmit:
132                                 str = "tx";
133                                 break;
134                 } // end-switch
135                 return str;
136         } // end-method
137
138         static String B64EncodeForXML(String data) {
139                 return Mime.base64Encode(data);
140         } // end-method
141         
142         public static void logProxyEvent(String eventText, String token) {
143                 if (DiagLevel.getLevel(Mod.proxy) == DetailLevel.OFF || !token.equals(KeyStr)) {
144                         return;
145                 } // end-if
146
147                 String msg = SyncTrace.B64EncodeForXML(eventText);
148                 String xml = SyncTrace.encodeTraceMessage(SyncTrace.getBaseTicsDelta(), Mod.proxy, InterfaceActivityDirection.None, "<d>" + msg + "</d>");
149                 writeXmlTraceMessage(xml);
150         } // end-method
151
152         public static void logAppEvent(String eventText) {
153                 if (DiagLevel.getLevel(Mod.app) == DetailLevel.OFF) {
154                         return;
155                 } // end-if
156
157                 long timestamp = SyncTrace.getBaseTicsDelta();
158                 String msg = SyncTrace.B64EncodeForXML(eventText);
159                 String xml = SyncTrace.encodeTraceMessage(timestamp, Mod.app, InterfaceActivityDirection.None, "<d>" + msg + "</d>");
160                 writeXmlTraceMessage(xml);
161         } // end-method
162
163         public static void logRPCEvent(InterfaceActivityDirection msgDirection, RPCMessage rpcMsg, String token) {
164                 DetailLevel dl = DiagLevel.getLevel(Mod.rpc);
165                 if (dl == DetailLevel.OFF || !token.equals(KeyStr)) {
166                         return;
167                 } // end-if
168
169                 long timestamp = SyncTrace.getBaseTicsDelta();
170                 String xml = SyncTrace.encodeTraceMessage(timestamp, Mod.rpc, msgDirection, rpc2Xml(dl, rpcMsg));
171                 writeXmlTraceMessage(xml);
172         } // end-method
173
174         private static String rpc2Xml(DetailLevel dl, RPCMessage rpcMsg) {
175                 StringBuilder rpcAsXml = new StringBuilder();
176                 rpcAsXml.append("<op>");
177                 rpcAsXml.append(rpcMsg.getFunctionName());
178                 rpcAsXml.append("</op>");
179                 boolean hasCorrelationID = false;
180                 Integer correlationID = -1;
181                 if (rpcMsg instanceof RPCRequest) {
182                         hasCorrelationID = true;
183                         correlationID = ((RPCRequest)rpcMsg).getCorrelationID();
184                 } else if (rpcMsg instanceof RPCResponse) {
185                         hasCorrelationID = true;
186                         correlationID = ((RPCResponse)rpcMsg).getCorrelationID();
187                 } // end-if
188                 if (hasCorrelationID) {
189                         rpcAsXml.append("<cid>");
190                         rpcAsXml.append(correlationID);
191                         rpcAsXml.append("</cid>");
192                 } // end-if
193                 rpcAsXml.append("<type>");
194                 rpcAsXml.append(rpcMsg.getMessageType());
195                 rpcAsXml.append("</type>");
196
197                 if (dl == DetailLevel.VERBOSE) {
198                         OpenRPCMessage orpcmsg = new OpenRPCMessage(rpcMsg);
199                         String rpcParamList = orpcmsg.msgDump();
200                         String msg = SyncTrace.B64EncodeForXML(rpcParamList);
201                         rpcAsXml.append("<d>");
202                         rpcAsXml.append(msg);
203                         rpcAsXml.append("</d>");
204                 } // end-if
205                 return rpcAsXml.toString();
206         } // end-method
207
208         public static void logMarshallingEvent(InterfaceActivityDirection msgDirection, byte[] marshalledMessage, String token) {
209                 DetailLevel dl = DiagLevel.getLevel(Mod.mar);
210                 if (dl == DetailLevel.OFF || !token.equals(KeyStr)) {
211                         return;
212                 } // end-fif
213
214                 long timestamp = SyncTrace.getBaseTicsDelta();
215                 StringBuilder msg = new StringBuilder();
216                 msg.append("<sz>");
217                 msg.append(marshalledMessage.length);
218                 msg.append("</sz>");
219                 if (dl == DetailLevel.VERBOSE) {
220                         msg.append("<d>");
221                         msg.append(Mime.base64Encode(marshalledMessage));
222                         msg.append("</d>");
223                 } // end-if
224                 String xml = SyncTrace.encodeTraceMessage(timestamp, Mod.mar, msgDirection, msg.toString());
225                 writeXmlTraceMessage(xml);
226         } // end-method
227
228         public static void logProtocolEvent(InterfaceActivityDirection frameDirection, ProtocolFrameHeader frameHeader, byte[] frameData, int frameDataOffset, int frameDataLength, String token) {
229                 DetailLevel dl = DiagLevel.getLevel(Mod.proto);
230                 if (dl == DetailLevel.OFF || !token.equals(KeyStr)) {
231                         return;
232                 } // end-if
233
234                 StringBuffer protoMsg = new StringBuffer();
235                 protoMsg.append("<frame>");
236                 protoMsg.append(SyncTrace.getProtocolFrameHeaderInfo(frameHeader, frameData));
237                 if (dl == DetailLevel.VERBOSE) {
238                         if (frameData != null && frameDataLength > 0) {
239                                 protoMsg.append("<d>");
240                                 String bytesInfo = "";
241                                 bytesInfo = Mime.base64Encode(frameData, frameDataOffset, frameDataLength);
242                                 protoMsg.append(bytesInfo);
243                                 protoMsg.append("</d>");
244                         } // end-if
245                 } // end-if
246                 protoMsg.append("</frame>");
247                 String xml = SyncTrace.encodeTraceMessage(SyncTrace.getBaseTicsDelta(), Mod.proto, frameDirection, protoMsg.toString());
248                 writeXmlTraceMessage(xml);
249         } // end-method
250
251         private static String getProtocolFrameType(FrameType f) {
252                 if (f == FrameType.Control)
253                         return "Control";
254                 else if (f == FrameType.Consecutive)
255                         return "Consecutive";
256                 else if (f == FrameType.First)
257                         return "First";
258                 else if (f == FrameType.Single)
259                         return "Single";
260
261                 return "Unknown";
262         } // end-method
263
264         private static String getProtocolSessionType(SessionType serviceType) {
265                 String s;
266                 if (serviceType == SessionType.RPC )
267                         s = "rpc";
268                 else if (serviceType == SessionType.Bulk_Data)
269                         s = "bulk";
270                 else
271                         s = "Unknown";
272                 return s;
273         } // end-method
274
275         private static String getProtocolFrameHeaderInfo(ProtocolFrameHeader hdr, byte[] buf) {
276                 StringBuilder sb = new StringBuilder();
277                 sb.append("<hdr>");
278                 sb.append("<ver>");
279                 sb.append(hdr.getVersion());
280                 sb.append("</ver><cmp>");
281                 sb.append(hdr.isCompressed());
282                 sb.append("</cmp><ft>");
283                 sb.append(getProtocolFrameType(hdr.getFrameType()));
284                 sb.append("</ft><st>");
285                 sb.append(getProtocolSessionType(hdr.getSessionType()));
286                 sb.append("</st><sid>");
287                 sb.append(hdr.getSessionID());
288                 sb.append("</sid><sz>");
289                 sb.append(hdr.getDataSize());
290                 sb.append("</sz>");
291
292                 int frameData = hdr.getFrameData();
293                 if (hdr.getFrameType() == FrameType.Control) {
294                         sb.append("<ca>");
295                         if (frameData == FrameDataControlFrameType.StartSession.getValue()) 
296                                 sb.append("StartSession");
297                         else if (frameData == FrameDataControlFrameType.StartSessionACK.getValue())
298                                 sb.append("StartSessionACK");
299                         else if (frameData == FrameDataControlFrameType.StartSessionNACK.getValue())
300                                 sb.append("StartSessionNACK");
301                         else if (frameData == FrameDataControlFrameType.EndSession.getValue())
302                                 sb.append("EndSession");
303                         sb.append("</ca>");
304                 } else if (hdr.getFrameType() == FrameType.Consecutive ) {
305                         sb.append("<fsn>");
306                         if (frameData == 0 )
307                                 sb.append("lastFrame");
308                         else
309                                 sb.append(String.format("%02X",frameData)); 
310                         sb.append("</fsn>");
311                 } else if (hdr.getFrameType() == FrameType.First ) {
312                         int totalSize = BitConverter.intFromByteArray(buf, 0);                  
313                         int numFrames = BitConverter.intFromByteArray(buf, 4);
314                         sb.append("<total>" + totalSize + "</total><numframes>" + numFrames + "</numframes>");
315                 } else if (hdr.getFrameType() == FrameType.Single ) {
316                         sb.append("<single/>");
317                 }
318
319                 sb.append("</hdr>");
320
321                 return sb.toString();
322         } // end-method
323
324         public static String getBTDeviceInfo(BluetoothDevice btDevice) {
325                 StringBuilder sb = new StringBuilder();
326                 sb.append("<btp>");
327                 String btdn = btDevice.getName();
328                 sb.append("<btn>");
329                 sb.append(SyncTrace.B64EncodeForXML(btdn));
330                 sb.append("</btn>");
331                 sb.append("<bta>" + btDevice.getAddress() + "</bta>");
332                 sb.append("<bts>" + btDevice.getBondState() + "</bts>");
333                 sb.append("</btp>");
334                 return sb.toString();
335         } // end-method
336
337         public static void logTransportEvent(String preamble, String transportSpecificInfoXml, InterfaceActivityDirection msgDirection, byte buf[], int byteLength, String token) {
338                 logTransportEvent(preamble, transportSpecificInfoXml, msgDirection, buf, 0, byteLength, token);
339         } // end-method
340
341         private static void checkB64(String x, byte[] buf, int offset, int byteLength) {
342                 if ((x.length() % 4) != 0) {
343                         NativeLogTool.logWarning(SyncTrace.SYSTEM_LOG_TAG, "b64 string length (" + x.length() + ") isn't multiple of 4: buf.length=" + buf.length + ", offset=" + offset + ", len=" + byteLength);
344                 } // end-if
345         } // end-method
346
347         public static void logTransportEvent(String preamble, String transportSpecificInfoXml, InterfaceActivityDirection msgDirection, byte buf[], int offset, int byteLength, String token) {
348                 if (DiagLevel.getLevel(Mod.tran) == DetailLevel.OFF || !token.equals(KeyStr)) {
349                         return;
350                 } // end-if
351
352                 StringBuilder msg = new StringBuilder();
353                 if (transportSpecificInfoXml != null && transportSpecificInfoXml.length() > 0) {
354                         msg.append(transportSpecificInfoXml);
355                 } // end-if
356                 if (preamble != null && preamble.length() > 0) {
357                         msg.append("<desc>");
358                         msg.append(preamble);
359                         msg.append("</desc>");
360                 } // end-if
361                 if (buf != null) {
362                         msg.append("<sz>");
363                         msg.append(byteLength);
364                         msg.append("</sz>");
365                         DetailLevel dl = DiagLevel.getLevel(Mod.tran);
366                         if (dl == DetailLevel.VERBOSE) {
367                                 if (buf != null && byteLength > 0) {
368                                         msg.append("<d>");
369                                         String bytesInfo = Mime.base64Encode(buf, offset, byteLength);
370                                         checkB64(bytesInfo, buf, offset, byteLength);
371                                         msg.append(bytesInfo);
372                                         msg.append("</d>");
373                                 } // end-if
374                         } // end-if
375                 } // end-if
376                 String xml = SyncTrace.encodeTraceMessage(SyncTrace.getBaseTicsDelta(), Mod.tran, msgDirection, msg.toString());
377                 writeXmlTraceMessage(xml);
378         } // end-method
379
380         // Package-scoped
381         static long getBaseTicsDelta() {
382                 return java.lang.System.currentTimeMillis() - getBaseTics();
383         }
384
385         // Package-scoped
386         static long getBaseTics() {
387                 return baseTics;
388         } // end-method
389
390         public static Boolean writeMessageToSiphonServer(String info) {
391                 return SiphonServer.sendFormattedTraceMessage(info);
392         }
393
394         private static void writeXmlTraceMessage(String msg) {
395                 try {                   
396                         // Attempt to write formatted message to the Siphon
397                         if (false == writeMessageToSiphonServer(msg)) {
398                                 // If writing to the Siphon fails, write to the native log
399                                 NativeLogTool.logInfo(SyncTrace.SYSTEM_LOG_TAG, msg);
400                         }
401                         
402                         ISTListener localTraceListener = m_appTraceListener;
403
404                         if (localTraceListener != null) {
405                                 try {
406                                         localTraceListener.logXmlMsg(msg, KeyStr);
407                                 } catch (Exception ex) {
408                                         DebugTool.logError("Failure calling ISTListener: " + ex.toString(), ex);
409                                 } // end-catch
410                         } // end-if
411                 } catch (Exception ex) {
412                         NativeLogTool.logError(SyncTrace.SYSTEM_LOG_TAG, "Failure writing XML trace message: " + ex.toString());
413                 }
414         } // end-method
415         
416         // Package-scoped
417         public static String getLogHeader(String dumpReason, int seqNo) {
418                 final String Sep = "-";
419                 StringBuilder write = new StringBuilder("<?xml version=\"1.0\"?>" + "<logs>");
420                 write.append("<info>");
421                 StringBuilder infoBlock = new StringBuilder();
422                 String hostInfo = Build.BRAND + Sep + Build.MANUFACTURER + Sep + Build.MODEL + "(" + Build.HOST + ")";
423                 infoBlock.append("<host>" + SyncTrace.B64EncodeForXML(hostInfo) + "</host>");
424                 String osv = Build.VERSION.RELEASE + " (" + Build.VERSION.CODENAME + ")";
425                 infoBlock.append("<osv>" + SyncTrace.B64EncodeForXML(osv) + "</osv>");
426                 infoBlock.append(TraceDeviceInfo.getTelephonyHeader());
427
428                 long heapSize = Debug.getNativeHeapFreeSize() / 1024;
429                 long heapAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
430                 infoBlock.append("<mem><hf>" + heapSize + "KB</hf><ha>" + heapAllocated + "KB</ha></mem>");
431                 infoBlock.append("<np>" + Runtime.getRuntime().availableProcessors() + "</np>");
432                 infoBlock.append("<pid>" + Process.myPid() + "</pid>");
433                 infoBlock.append("<tid>" + Thread.currentThread().getId() + "</tid>");
434
435                 Timestamp stamp = new Timestamp(SyncTrace.getBaseTics());
436                 String GMTtime = stamp.toGMTString().substring(0, 19);
437                 long fracSec = stamp.getNanos() / 1000000; // divide by a million
438                 String fracSecStr = String.format("%03d", fracSec);
439                 infoBlock.append("<utc>" + GMTtime + "." + fracSecStr + "</utc>");
440
441                 infoBlock.append(TraceDeviceInfo.getLogHeaderBluetoothPairs());
442                 infoBlock.append(getSmartDeviceLinkTraceRoot(dumpReason, seqNo));
443
444                 write.append(infoBlock);
445
446                 write.append("</info>" + "<msgs>");
447                 return write.toString();
448         } // end-method
449         
450         private static String getSmartDeviceLinkTraceRoot(String dumpReason, int seqNo) {
451                 StringBuilder write = new StringBuilder("<SmartDeviceLinktraceroot>" + "<sequencenum>" + seqNo
452                                 + "</sequencenum>" + "<dumpreason>" + dumpReason
453                                 + "</dumpreason><tracelevel>");
454
455                 write.append("<tran>" + DiagLevel.getLevel(Mod.tran) + "</tran>");
456                 write.append("<proto>" + DiagLevel.getLevel(Mod.proto) + "</proto>");
457                 write.append("<mar>" + DiagLevel.getLevel(Mod.mar) + "</mar>");
458                 write.append("<rpc>" + DiagLevel.getLevel(Mod.rpc) + "</rpc>");
459                 write.append("<proxy>" + DiagLevel.getLevel(Mod.proxy) + "</proxy>");
460                 write.append("<app>" + DiagLevel.getLevel(Mod.app) + "</app>");
461
462                 write.append("</tracelevel>");
463                 write.append("</SmartDeviceLinktraceroot>");
464                 return write.toString();
465         } // end-method
466 } // end-class