2 // Copyright (c) 2013 Ford Motor Company
4 package com.smartdevicelink.trace;
6 import java.sql.Timestamp;
8 import android.bluetooth.BluetoothDevice;
9 import android.os.Build;
10 import android.os.Debug;
11 import android.os.Process;
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;
28 /* This class handles the global TraceSettings as requested by the users either through the combination of the following
30 2. Application XML config
31 3. Programmatic requests from application itself
33 It is manifested in the <SmartDeviceLink>...</SmartDeviceLink> tags
36 public class SyncTrace {
37 private static final String SmartDeviceLink_LIB_TRACE_KEY = "42baba60-eb57-11df-98cf-0800200c9a66";
39 static boolean canWriteLogs = false;
41 public static final String SYSTEM_LOG_TAG = "SyncTrace";
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;
47 protected static ISTListener m_appTraceListener = null;
50 /// The PUBLIC interface to SyncTrace starts here
54 public static void setAcceptAPITraceAdjustments(Boolean APITraceAdjustmentsAccepted) {
55 if (APITraceAdjustmentsAccepted != null) {
56 acceptAPITraceAdjustments = APITraceAdjustmentsAccepted;
60 public static boolean getAcceptAPITraceAdjustments() {
61 return acceptAPITraceAdjustments;
64 public static void setAppTraceListener(ISTListener listener) {
65 m_appTraceListener = listener;
68 public static void setTracingEnable(Boolean enable) {
70 canWriteLogs = enable;
74 public static void setAppTraceLevel(DetailLevel dt) {
75 if ( dt != null && acceptAPITraceAdjustments)
76 DiagLevel.setLevel(Mod.app, dt);
79 public static void setProxyTraceLevel(DetailLevel dt) {
80 if (dt != null && acceptAPITraceAdjustments)
81 DiagLevel.setLevel(Mod.proxy, dt);
84 public static void setRpcTraceLevel(DetailLevel dt) {
85 if (dt != null && acceptAPITraceAdjustments)
86 DiagLevel.setLevel(Mod.rpc, dt);
89 public static void setMarshallingTraceLevel(DetailLevel dt) {
90 if (dt != null && acceptAPITraceAdjustments)
91 DiagLevel.setLevel(Mod.mar, dt);
94 public static void setProtocolTraceLevel(DetailLevel dt) {
95 if (dt != null && acceptAPITraceAdjustments)
96 DiagLevel.setLevel(Mod.proto, dt);
99 public static void setTransportTraceLevel(DetailLevel dt) {
100 if (dt != null && acceptAPITraceAdjustments)
101 DiagLevel.setLevel(Mod.tran, dt);
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());
114 if (msgDirection != InterfaceActivityDirection.None) {
116 sb.append(interfaceActivityDirectionToString(msgDirection));
119 sb.append(msgBodyXml);
122 return sb.toString();
125 private static String interfaceActivityDirectionToString(InterfaceActivityDirection iaDirection) {
127 switch (iaDirection) {
138 static String B64EncodeForXML(String data) {
139 return Mime.base64Encode(data);
142 public static void logProxyEvent(String eventText, String token) {
143 if (DiagLevel.getLevel(Mod.proxy) == DetailLevel.OFF || !token.equals(KeyStr)) {
147 String msg = SyncTrace.B64EncodeForXML(eventText);
148 String xml = SyncTrace.encodeTraceMessage(SyncTrace.getBaseTicsDelta(), Mod.proxy, InterfaceActivityDirection.None, "<d>" + msg + "</d>");
149 writeXmlTraceMessage(xml);
152 public static void logAppEvent(String eventText) {
153 if (DiagLevel.getLevel(Mod.app) == DetailLevel.OFF) {
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);
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)) {
169 long timestamp = SyncTrace.getBaseTicsDelta();
170 String xml = SyncTrace.encodeTraceMessage(timestamp, Mod.rpc, msgDirection, rpc2Xml(dl, rpcMsg));
171 writeXmlTraceMessage(xml);
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();
188 if (hasCorrelationID) {
189 rpcAsXml.append("<cid>");
190 rpcAsXml.append(correlationID);
191 rpcAsXml.append("</cid>");
193 rpcAsXml.append("<type>");
194 rpcAsXml.append(rpcMsg.getMessageType());
195 rpcAsXml.append("</type>");
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>");
205 return rpcAsXml.toString();
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)) {
214 long timestamp = SyncTrace.getBaseTicsDelta();
215 StringBuilder msg = new StringBuilder();
217 msg.append(marshalledMessage.length);
219 if (dl == DetailLevel.VERBOSE) {
221 msg.append(Mime.base64Encode(marshalledMessage));
224 String xml = SyncTrace.encodeTraceMessage(timestamp, Mod.mar, msgDirection, msg.toString());
225 writeXmlTraceMessage(xml);
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)) {
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>");
246 protoMsg.append("</frame>");
247 String xml = SyncTrace.encodeTraceMessage(SyncTrace.getBaseTicsDelta(), Mod.proto, frameDirection, protoMsg.toString());
248 writeXmlTraceMessage(xml);
251 private static String getProtocolFrameType(FrameType f) {
252 if (f == FrameType.Control)
254 else if (f == FrameType.Consecutive)
255 return "Consecutive";
256 else if (f == FrameType.First)
258 else if (f == FrameType.Single)
264 private static String getProtocolSessionType(SessionType serviceType) {
266 if (serviceType == SessionType.RPC )
268 else if (serviceType == SessionType.Bulk_Data)
275 private static String getProtocolFrameHeaderInfo(ProtocolFrameHeader hdr, byte[] buf) {
276 StringBuilder sb = new StringBuilder();
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());
292 int frameData = hdr.getFrameData();
293 if (hdr.getFrameType() == FrameType.Control) {
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");
304 } else if (hdr.getFrameType() == FrameType.Consecutive ) {
307 sb.append("lastFrame");
309 sb.append(String.format("%02X",frameData));
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/>");
321 return sb.toString();
324 public static String getBTDeviceInfo(BluetoothDevice btDevice) {
325 StringBuilder sb = new StringBuilder();
327 String btdn = btDevice.getName();
329 sb.append(SyncTrace.B64EncodeForXML(btdn));
331 sb.append("<bta>" + btDevice.getAddress() + "</bta>");
332 sb.append("<bts>" + btDevice.getBondState() + "</bts>");
334 return sb.toString();
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);
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);
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)) {
352 StringBuilder msg = new StringBuilder();
353 if (transportSpecificInfoXml != null && transportSpecificInfoXml.length() > 0) {
354 msg.append(transportSpecificInfoXml);
356 if (preamble != null && preamble.length() > 0) {
357 msg.append("<desc>");
358 msg.append(preamble);
359 msg.append("</desc>");
363 msg.append(byteLength);
365 DetailLevel dl = DiagLevel.getLevel(Mod.tran);
366 if (dl == DetailLevel.VERBOSE) {
367 if (buf != null && byteLength > 0) {
369 String bytesInfo = Mime.base64Encode(buf, offset, byteLength);
370 checkB64(bytesInfo, buf, offset, byteLength);
371 msg.append(bytesInfo);
376 String xml = SyncTrace.encodeTraceMessage(SyncTrace.getBaseTicsDelta(), Mod.tran, msgDirection, msg.toString());
377 writeXmlTraceMessage(xml);
381 static long getBaseTicsDelta() {
382 return java.lang.System.currentTimeMillis() - getBaseTics();
386 static long getBaseTics() {
390 public static Boolean writeMessageToSiphonServer(String info) {
391 return SiphonServer.sendFormattedTraceMessage(info);
394 private static void writeXmlTraceMessage(String msg) {
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);
402 ISTListener localTraceListener = m_appTraceListener;
404 if (localTraceListener != null) {
406 localTraceListener.logXmlMsg(msg, KeyStr);
407 } catch (Exception ex) {
408 DebugTool.logError("Failure calling ISTListener: " + ex.toString(), ex);
411 } catch (Exception ex) {
412 NativeLogTool.logError(SyncTrace.SYSTEM_LOG_TAG, "Failure writing XML trace message: " + ex.toString());
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());
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>");
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>");
441 infoBlock.append(TraceDeviceInfo.getLogHeaderBluetoothPairs());
442 infoBlock.append(getSmartDeviceLinkTraceRoot(dumpReason, seqNo));
444 write.append(infoBlock);
446 write.append("</info>" + "<msgs>");
447 return write.toString();
450 private static String getSmartDeviceLinkTraceRoot(String dumpReason, int seqNo) {
451 StringBuilder write = new StringBuilder("<SmartDeviceLinktraceroot>" + "<sequencenum>" + seqNo
452 + "</sequencenum>" + "<dumpreason>" + dumpReason
453 + "</dumpreason><tracelevel>");
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>");
462 write.append("</tracelevel>");
463 write.append("</SmartDeviceLinktraceroot>");
464 return write.toString();