1 package com.smartdevicelink.trace;
\r
3 import java.sql.Timestamp;
\r
5 import android.bluetooth.BluetoothDevice;
\r
6 import android.os.Build;
\r
7 import android.os.Debug;
\r
8 import android.os.Process;
\r
10 import com.smartdevicelink.protocol.ProtocolFrameHeader;
\r
11 import com.smartdevicelink.protocol.enums.FrameDataControlFrameType;
\r
12 import com.smartdevicelink.protocol.enums.FrameType;
\r
13 import com.smartdevicelink.protocol.enums.SessionType;
\r
14 import com.smartdevicelink.proxy.RPCMessage;
\r
15 import com.smartdevicelink.proxy.RPCRequest;
\r
16 import com.smartdevicelink.proxy.RPCResponse;
\r
17 import com.smartdevicelink.trace.enums.DetailLevel;
\r
18 import com.smartdevicelink.trace.enums.InterfaceActivityDirection;
\r
19 import com.smartdevicelink.trace.enums.Mod;
\r
20 import com.smartdevicelink.transport.SiphonServer;
\r
21 import com.smartdevicelink.util.BitConverter;
\r
22 import com.smartdevicelink.util.DebugTool;
\r
23 import com.smartdevicelink.util.NativeLogTool;
\r
25 /* This class handles the global TraceSettings as requested by the users either through the combination of the following
\r
27 2. Application XML config
\r
28 3. Programmatic requests from application itself
\r
30 It is manifested in the <applink>...</applink> tags
\r
33 public class SmartDeviceLinkTrace {
\r
34 private static final String SMARTDEVICELINK_LIB_TRACE_KEY = "42baba60-eb57-11df-98cf-0800200c9a66";
\r
36 static boolean canWriteLogs = false;
\r
38 public static final String SYSTEM_LOG_TAG = "SmartDeviceLinkTrace";
\r
40 private static long baseTics = java.lang.System.currentTimeMillis();
\r
41 private final static String KeyStr = SMARTDEVICELINK_LIB_TRACE_KEY;
\r
42 private static boolean acceptAPITraceAdjustments = true;
\r
44 protected static ISTListener m_appTraceListener = null;
\r
47 /// The PUBLIC interface to SmartDeviceLinkTrace starts here
\r
51 public static void setAcceptAPITraceAdjustments(Boolean APITraceAdjustmentsAccepted) {
\r
52 if (APITraceAdjustmentsAccepted != null) {
\r
53 acceptAPITraceAdjustments = APITraceAdjustmentsAccepted;
\r
57 public static boolean getAcceptAPITraceAdjustments() {
\r
58 return acceptAPITraceAdjustments;
\r
61 public static void setAppTraceListener(ISTListener listener) {
\r
62 m_appTraceListener = listener;
\r
65 public static void setTracingEnable(Boolean enable) {
\r
66 if (enable != null) {
\r
67 canWriteLogs = enable;
\r
71 public static void setAppTraceLevel(DetailLevel dt) {
\r
72 if ( dt != null && acceptAPITraceAdjustments)
\r
73 DiagLevel.setLevel(Mod.app, dt);
\r
76 public static void setProxyTraceLevel(DetailLevel dt) {
\r
77 if (dt != null && acceptAPITraceAdjustments)
\r
78 DiagLevel.setLevel(Mod.proxy, dt);
\r
81 public static void setRpcTraceLevel(DetailLevel dt) {
\r
82 if (dt != null && acceptAPITraceAdjustments)
\r
83 DiagLevel.setLevel(Mod.rpc, dt);
\r
86 public static void setMarshallingTraceLevel(DetailLevel dt) {
\r
87 if (dt != null && acceptAPITraceAdjustments)
\r
88 DiagLevel.setLevel(Mod.mar, dt);
\r
91 public static void setProtocolTraceLevel(DetailLevel dt) {
\r
92 if (dt != null && acceptAPITraceAdjustments)
\r
93 DiagLevel.setLevel(Mod.proto, dt);
\r
96 public static void setTransportTraceLevel(DetailLevel dt) {
\r
97 if (dt != null && acceptAPITraceAdjustments)
\r
98 DiagLevel.setLevel(Mod.tran, dt);
\r
101 private static String encodeTraceMessage(long timestamp, Mod module, InterfaceActivityDirection msgDirection, String msgBodyXml) {
\r
102 StringBuilder sb = new StringBuilder("<msg><dms>");
\r
103 sb.append(timestamp);
\r
104 sb.append("</dms><pid>");
\r
105 sb.append(Process.myPid());
\r
106 sb.append("</pid><tid>");
\r
107 sb.append(Thread.currentThread().getId());
\r
108 sb.append("</tid><mod>");
\r
109 sb.append(module.toString());
\r
110 sb.append("</mod>");
\r
111 if (msgDirection != InterfaceActivityDirection.None) {
\r
112 sb.append("<dir>");
\r
113 sb.append(interfaceActivityDirectionToString(msgDirection));
\r
114 sb.append("</dir>");
\r
116 sb.append(msgBodyXml);
\r
117 sb.append("</msg>");
\r
119 return sb.toString();
\r
122 private static String interfaceActivityDirectionToString(InterfaceActivityDirection iaDirection) {
\r
124 switch (iaDirection) {
\r
135 static String B64EncodeForXML(String data) {
\r
136 return Mime.base64Encode(data);
\r
137 // Base64 only available in 2.2, when SmartDeviceLink base is 2.2 use: return Base64.encodeToString(data.getBytes(), Base64.DEFAULT);
\r
140 public static void logProxyEvent(String eventText, String token) {
\r
141 if (DiagLevel.getLevel(Mod.proxy) == DetailLevel.OFF || !token.equals(KeyStr)) {
\r
145 String msg = SmartDeviceLinkTrace.B64EncodeForXML(eventText);
\r
146 String xml = SmartDeviceLinkTrace.encodeTraceMessage(SmartDeviceLinkTrace.getBaseTicsDelta(), Mod.proxy, InterfaceActivityDirection.None, "<d>" + msg + "</d>");
\r
147 writeXmlTraceMessage(xml);
\r
150 public static void logAppEvent(String eventText) {
\r
151 if (DiagLevel.getLevel(Mod.app) == DetailLevel.OFF) {
\r
155 long timestamp = SmartDeviceLinkTrace.getBaseTicsDelta();
\r
156 String msg = SmartDeviceLinkTrace.B64EncodeForXML(eventText);
\r
157 String xml = SmartDeviceLinkTrace.encodeTraceMessage(timestamp, Mod.app, InterfaceActivityDirection.None, "<d>" + msg + "</d>");
\r
158 writeXmlTraceMessage(xml);
\r
161 public static void logRPCEvent(InterfaceActivityDirection msgDirection, RPCMessage rpcMsg, String token) {
\r
162 DetailLevel dl = DiagLevel.getLevel(Mod.rpc);
\r
163 if (dl == DetailLevel.OFF || !token.equals(KeyStr)) {
\r
167 long timestamp = SmartDeviceLinkTrace.getBaseTicsDelta();
\r
168 String xml = SmartDeviceLinkTrace.encodeTraceMessage(timestamp, Mod.rpc, msgDirection, rpc2Xml(dl, rpcMsg));
\r
169 writeXmlTraceMessage(xml);
\r
172 private static String rpc2Xml(DetailLevel dl, RPCMessage rpcMsg) {
\r
173 StringBuilder rpcAsXml = new StringBuilder();
\r
174 rpcAsXml.append("<op>");
\r
175 rpcAsXml.append(rpcMsg.getFunctionName());
\r
176 rpcAsXml.append("</op>");
\r
177 boolean hasCorrelationID = false;
\r
178 Integer correlationID = -1;
\r
179 if (rpcMsg instanceof RPCRequest) {
\r
180 hasCorrelationID = true;
\r
181 correlationID = ((RPCRequest)rpcMsg).getCorrelationID();
\r
182 } else if (rpcMsg instanceof RPCResponse) {
\r
183 hasCorrelationID = true;
\r
184 correlationID = ((RPCResponse)rpcMsg).getCorrelationID();
\r
186 if (hasCorrelationID) {
\r
187 rpcAsXml.append("<cid>");
\r
188 rpcAsXml.append(correlationID);
\r
189 rpcAsXml.append("</cid>");
\r
191 rpcAsXml.append("<type>");
\r
192 rpcAsXml.append(rpcMsg.getMessageType());
\r
193 rpcAsXml.append("</type>");
\r
194 //rpcAsXml.append(newline);
\r
196 if (dl == DetailLevel.VERBOSE) {
\r
197 OpenRPCMessage orpcmsg = new OpenRPCMessage(rpcMsg);
\r
198 String rpcParamList = orpcmsg.msgDump();
\r
199 String msg = SmartDeviceLinkTrace.B64EncodeForXML(rpcParamList);
\r
200 rpcAsXml.append("<d>");
\r
201 rpcAsXml.append(msg);
\r
202 rpcAsXml.append("</d>");
\r
204 return rpcAsXml.toString();
\r
207 public static void logMarshallingEvent(InterfaceActivityDirection msgDirection, byte[] marshalledMessage, String token) {
\r
208 DetailLevel dl = DiagLevel.getLevel(Mod.mar);
\r
209 if (dl == DetailLevel.OFF || !token.equals(KeyStr)) {
\r
213 long timestamp = SmartDeviceLinkTrace.getBaseTicsDelta();
\r
214 StringBuilder msg = new StringBuilder();
\r
215 msg.append("<sz>");
\r
216 msg.append(marshalledMessage.length);
\r
217 msg.append("</sz>");
\r
218 if (dl == DetailLevel.VERBOSE) {
\r
220 msg.append(Mime.base64Encode(marshalledMessage));
\r
221 // Base64 only available in 2.2, when SmartDeviceLink base is 2.2 use: msg.append(Base64.encodeToString(marshalledMessage, Base64.DEFAULT));
\r
222 msg.append("</d>");
\r
224 String xml = SmartDeviceLinkTrace.encodeTraceMessage(timestamp, Mod.mar, msgDirection, msg.toString());
\r
225 writeXmlTraceMessage(xml);
\r
228 public static void logProtocolEvent(InterfaceActivityDirection frameDirection, ProtocolFrameHeader frameHeader, byte[] frameData, int frameDataOffset, int frameDataLength, String token) {
\r
229 DetailLevel dl = DiagLevel.getLevel(Mod.proto);
\r
230 if (dl == DetailLevel.OFF || !token.equals(KeyStr)) {
\r
234 StringBuffer protoMsg = new StringBuffer();
\r
235 protoMsg.append("<frame>");
\r
236 protoMsg.append(SmartDeviceLinkTrace.getProtocolFrameHeaderInfo(frameHeader, frameData));
\r
237 if (dl == DetailLevel.VERBOSE) {
\r
238 if (frameData != null && frameDataLength > 0) {
\r
239 protoMsg.append("<d>");
\r
240 String bytesInfo = "";
\r
241 bytesInfo = Mime.base64Encode(frameData, frameDataOffset, frameDataLength);
\r
242 // Base64 only available in 2.2, when SmartDeviceLink base is 2.2 use: bytesInfo = Base64.encodeToString(frameData, frameDataOffset, frameDataLength, Base64.DEFAULT);
\r
243 protoMsg.append(bytesInfo);
\r
244 protoMsg.append("</d>");
\r
247 protoMsg.append("</frame>");
\r
248 String xml = SmartDeviceLinkTrace.encodeTraceMessage(SmartDeviceLinkTrace.getBaseTicsDelta(), Mod.proto, frameDirection, protoMsg.toString());
\r
249 writeXmlTraceMessage(xml);
\r
252 private static String getProtocolFrameType(FrameType f) {
\r
253 if (f == FrameType.Control)
\r
255 else if (f == FrameType.Consecutive)
\r
256 return "Consecutive";
\r
257 else if (f == FrameType.First)
\r
259 else if (f == FrameType.Single)
\r
265 private static String getProtocolSessionType(SessionType serviceType) {
\r
267 if (serviceType == SessionType.RPC )
\r
269 else if (serviceType == SessionType.Bulk_Data)
\r
276 private static String getProtocolFrameHeaderInfo(ProtocolFrameHeader hdr, byte[] buf) {
\r
277 StringBuilder sb = new StringBuilder();
\r
278 sb.append("<hdr>");
\r
279 sb.append("<ver>");
\r
280 sb.append(hdr.getVersion());
\r
281 sb.append("</ver><cmp>");
\r
282 sb.append(hdr.isCompressed());
\r
283 sb.append("</cmp><ft>");
\r
284 sb.append(getProtocolFrameType(hdr.getFrameType()));
\r
285 sb.append("</ft><st>");
\r
286 sb.append(getProtocolSessionType(hdr.getSessionType()));
\r
287 sb.append("</st><sid>");
\r
288 sb.append(hdr.getSessionID());
\r
289 sb.append("</sid><sz>");
\r
290 sb.append(hdr.getDataSize());
\r
291 sb.append("</sz>");
\r
293 int frameData = hdr.getFrameData();
\r
294 if (hdr.getFrameType() == FrameType.Control) {
\r
296 if (frameData == FrameDataControlFrameType.StartSession.getValue())
\r
297 sb.append("StartSession");
\r
298 else if (frameData == FrameDataControlFrameType.StartSessionACK.getValue())
\r
299 sb.append("StartSessionACK");
\r
300 else if (frameData == FrameDataControlFrameType.StartSessionNACK.getValue())
\r
301 sb.append("StartSessionNACK");
\r
302 else if (frameData == FrameDataControlFrameType.EndSession.getValue())
\r
303 sb.append("EndSession");
\r
304 sb.append("</ca>");
\r
305 } else if (hdr.getFrameType() == FrameType.Consecutive ) {
\r
306 sb.append("<fsn>");
\r
307 if (frameData == 0 )
\r
308 sb.append("lastFrame");
\r
310 sb.append(String.format("%02X",frameData));
\r
311 sb.append("</fsn>");
\r
312 } else if (hdr.getFrameType() == FrameType.First ) {
\r
313 int totalSize = BitConverter.intFromByteArray(buf, 0);
\r
314 int numFrames = BitConverter.intFromByteArray(buf, 4);
\r
315 sb.append("<total>" + totalSize + "</total><numframes>" + numFrames + "</numframes>");
\r
316 } else if (hdr.getFrameType() == FrameType.Single ) {
\r
317 sb.append("<single/>");
\r
320 sb.append("</hdr>");
\r
322 return sb.toString();
\r
325 public static String getBTDeviceInfo(BluetoothDevice btDevice) {
\r
326 StringBuilder sb = new StringBuilder();
\r
327 sb.append("<btp>");
\r
328 String btdn = btDevice.getName();
\r
329 sb.append("<btn>");
\r
330 sb.append(SmartDeviceLinkTrace.B64EncodeForXML(btdn));
\r
331 sb.append("</btn>");
\r
332 sb.append("<bta>" + btDevice.getAddress() + "</bta>");
\r
333 sb.append("<bts>" + btDevice.getBondState() + "</bts>");
\r
334 sb.append("</btp>");
\r
335 return sb.toString();
\r
338 public static void logTransportEvent(String preamble, String transportSpecificInfoXml, InterfaceActivityDirection msgDirection, byte buf[], int byteLength, String token) {
\r
339 logTransportEvent(preamble, transportSpecificInfoXml, msgDirection, buf, 0, byteLength, token);
\r
342 private static void checkB64(String x, byte[] buf, int offset, int byteLength) {
\r
343 if ((x.length() % 4) != 0) {
\r
344 NativeLogTool.logWarning(SmartDeviceLinkTrace.SYSTEM_LOG_TAG, "b64 string length (" + x.length() + ") isn't multiple of 4: buf.length=" + buf.length + ", offset=" + offset + ", len=" + byteLength);
\r
348 public static void logTransportEvent(String preamble, String transportSpecificInfoXml, InterfaceActivityDirection msgDirection, byte buf[], int offset, int byteLength, String token) {
\r
349 if (DiagLevel.getLevel(Mod.tran) == DetailLevel.OFF || !token.equals(KeyStr)) {
\r
353 StringBuilder msg = new StringBuilder();
\r
354 if (transportSpecificInfoXml != null && transportSpecificInfoXml.length() > 0) {
\r
355 msg.append(transportSpecificInfoXml);
\r
357 if (preamble != null && preamble.length() > 0) {
\r
358 msg.append("<desc>");
\r
359 msg.append(preamble);
\r
360 msg.append("</desc>");
\r
363 msg.append("<sz>");
\r
364 msg.append(byteLength);
\r
365 msg.append("</sz>");
\r
366 DetailLevel dl = DiagLevel.getLevel(Mod.tran);
\r
367 if (dl == DetailLevel.VERBOSE) {
\r
368 if (buf != null && byteLength > 0) {
\r
370 String bytesInfo = Mime.base64Encode(buf, offset, byteLength);
\r
371 checkB64(bytesInfo, buf, offset, byteLength);
\r
372 msg.append(bytesInfo);
\r
373 msg.append("</d>");
\r
377 String xml = SmartDeviceLinkTrace.encodeTraceMessage(SmartDeviceLinkTrace.getBaseTicsDelta(), Mod.tran, msgDirection, msg.toString());
\r
378 writeXmlTraceMessage(xml);
\r
382 static long getBaseTicsDelta() {
\r
383 return java.lang.System.currentTimeMillis() - getBaseTics();
\r
387 static long getBaseTics() {
\r
391 public static Boolean writeMessageToSiphonServer(String info) {
\r
392 return SiphonServer.sendFormattedTraceMessage(info);
\r
395 private static void writeXmlTraceMessage(String msg) {
\r
397 // Attempt to write formatted message to the Siphon
\r
398 if (false == writeMessageToSiphonServer(msg)) {
\r
399 // If writing to the Siphon fails, write to the native log
\r
400 NativeLogTool.logInfo(SmartDeviceLinkTrace.SYSTEM_LOG_TAG, msg);
\r
403 ISTListener localTraceListener = m_appTraceListener;
\r
405 if (localTraceListener != null) {
\r
407 localTraceListener.logXmlMsg(msg, KeyStr);
\r
408 } catch (Exception ex) {
\r
409 DebugTool.logError("Failure calling ISTListener: " + ex.toString(), ex);
\r
412 } catch (Exception ex) {
\r
413 NativeLogTool.logError(SmartDeviceLinkTrace.SYSTEM_LOG_TAG, "Failure writing XML trace message: " + ex.toString());
\r
418 public static String getLogHeader(String dumpReason, int seqNo) {
\r
419 final String Sep = "-";
\r
420 StringBuilder write = new StringBuilder("<?xml version=\"1.0\"?>" + "<logs>");
\r
421 write.append("<info>");
\r
422 StringBuilder infoBlock = new StringBuilder();
\r
423 String hostInfo = Build.BRAND + Sep + Build.MANUFACTURER + Sep + Build.MODEL + "(" + Build.HOST + ")";
\r
424 infoBlock.append("<host>" + SmartDeviceLinkTrace.B64EncodeForXML(hostInfo) + "</host>");
\r
425 String osv = Build.VERSION.RELEASE + " (" + Build.VERSION.CODENAME + ")";
\r
426 infoBlock.append("<osv>" + SmartDeviceLinkTrace.B64EncodeForXML(osv) + "</osv>");
\r
427 infoBlock.append(TraceDeviceInfo.getTelephonyHeader());
\r
429 long heapSize = Debug.getNativeHeapFreeSize() / 1024;
\r
430 long heapAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
\r
431 infoBlock.append("<mem><hf>" + heapSize + "KB</hf><ha>" + heapAllocated + "KB</ha></mem>");
\r
432 infoBlock.append("<np>" + Runtime.getRuntime().availableProcessors() + "</np>");
\r
433 infoBlock.append("<pid>" + Process.myPid() + "</pid>");
\r
434 infoBlock.append("<tid>" + Thread.currentThread().getId() + "</tid>");
\r
436 // String dateStamp = (String)
\r
437 // DateFormat.format("yy-MM-dd hh:mm:ss SSS", new Timestamp(baseTics));
\r
438 Timestamp stamp = new Timestamp(SmartDeviceLinkTrace.getBaseTics());
\r
439 String GMTtime = stamp.toGMTString().substring(0, 19);
\r
440 long fracSec = stamp.getNanos() / 1000000; // divide by a million
\r
441 String fracSecStr = String.format("%03d", fracSec);
\r
442 infoBlock.append("<utc>" + GMTtime + "." + fracSecStr + "</utc>");
\r
444 infoBlock.append(TraceDeviceInfo.getLogHeaderBluetoothPairs());
\r
445 infoBlock.append(getApplinkTraceRoot(dumpReason, seqNo));
\r
447 write.append(infoBlock);
\r
449 write.append("</info>" + "<msgs>");
\r
450 return write.toString();
\r
453 private static String getApplinkTraceRoot(String dumpReason, int seqNo) {
\r
454 StringBuilder write = new StringBuilder("<applinktraceroot>" + "<sequencenum>" + seqNo
\r
455 + "</sequencenum>" + "<dumpreason>" + dumpReason
\r
456 + "</dumpreason><tracelevel>");
\r
458 write.append("<tran>" + DiagLevel.getLevel(Mod.tran) + "</tran>");
\r
459 write.append("<proto>" + DiagLevel.getLevel(Mod.proto) + "</proto>");
\r
460 write.append("<mar>" + DiagLevel.getLevel(Mod.mar) + "</mar>");
\r
461 write.append("<rpc>" + DiagLevel.getLevel(Mod.rpc) + "</rpc>");
\r
462 write.append("<proxy>" + DiagLevel.getLevel(Mod.proxy) + "</proxy>");
\r
463 write.append("<app>" + DiagLevel.getLevel(Mod.app) + "</app>");
\r
465 write.append("</tracelevel>");
\r
466 write.append("</applinktraceroot>");
\r
467 return write.toString();
\r