1import { 2 CrossPlatform, 3 ShellTransitionData, 4 Transition, 5 TransitionChange, 6 TransitionType, 7 WmTransitionData, 8} from 'trace/flickerlib/common'; 9import {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp'; 10import {TraceType} from 'trace/trace_type'; 11import {AbstractParser} from './abstract_parser'; 12import {WmTransitionsTraceFileProto} from './proto_types'; 13 14export class ParserTransitionsWm extends AbstractParser { 15 private realToElapsedTimeOffsetNs: undefined | bigint; 16 17 override getTraceType(): TraceType { 18 return TraceType.WM_TRANSITION; 19 } 20 21 override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): any { 22 return this.parseWmTransitionEntry(entryProto); 23 } 24 25 override decodeTrace(buffer: Uint8Array): any[] { 26 const decodedProto = WmTransitionsTraceFileProto.decode(buffer) as any; 27 if (Object.prototype.hasOwnProperty.call(decodedProto, 'realToElapsedTimeOffsetNanos')) { 28 this.realToElapsedTimeOffsetNs = BigInt(decodedProto.realToElapsedTimeOffsetNanos); 29 } else { 30 console.warn('Missing realToElapsedTimeOffsetNanos property on SF trace proto'); 31 this.realToElapsedTimeOffsetNs = undefined; 32 } 33 return decodedProto.transitions; 34 } 35 36 override getMagicNumber(): number[] | undefined { 37 return [0x09, 0x54, 0x52, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TRNTRACE 38 } 39 40 override getTimestamp(type: TimestampType, decodedEntry: Transition): undefined | Timestamp { 41 decodedEntry = this.parseWmTransitionEntry(decodedEntry); 42 43 if (type === TimestampType.ELAPSED) { 44 return new ElapsedTimestamp(BigInt(decodedEntry.timestamp.elapsedNanos.toString())); 45 } 46 47 if (type === TimestampType.REAL) { 48 return new RealTimestamp(BigInt(decodedEntry.timestamp.unixNanos.toString())); 49 } 50 51 throw new Error('Timestamp type unsupported'); 52 } 53 54 private parseWmTransitionEntry(entry: any): Transition { 55 this.validateWmTransitionEntry(entry); 56 let changes: TransitionChange[] | null; 57 if (entry.targets.length === 0) { 58 changes = null; 59 } else { 60 changes = entry.targets.map( 61 (it: any) => 62 new TransitionChange(TransitionType.Companion.fromInt(it.mode), it.layerId, it.windowId) 63 ); 64 } 65 66 if (this.realToElapsedTimeOffsetNs === undefined) { 67 throw new Error('missing realToElapsedTimeOffsetNs'); 68 } 69 70 let createTime = null; 71 if (entry.createTimeNs && BigInt(entry.createTimeNs.toString()) !== 0n) { 72 const unixNs = BigInt(entry.createTimeNs.toString()) + this.realToElapsedTimeOffsetNs; 73 createTime = CrossPlatform.timestamp.fromString( 74 entry.createTimeNs.toString(), 75 null, 76 unixNs.toString() 77 ); 78 } 79 80 let sendTime = null; 81 if (entry.sendTimeNs && BigInt(entry.sendTimeNs.toString()) !== 0n) { 82 const unixNs = BigInt(entry.sendTimeNs.toString()) + this.realToElapsedTimeOffsetNs; 83 sendTime = CrossPlatform.timestamp.fromString( 84 entry.sendTimeNs.toString(), 85 null, 86 unixNs.toString() 87 ); 88 } 89 90 let abortTime = null; 91 if (entry.abortTimeNs && BigInt(entry.abortTimeNs.toString()) !== 0n) { 92 const unixNs = BigInt(entry.abortTimeNs.toString()) + this.realToElapsedTimeOffsetNs; 93 abortTime = CrossPlatform.timestamp.fromString( 94 entry.abortTimeNs.toString(), 95 null, 96 unixNs.toString() 97 ); 98 } 99 100 let finishTime = null; 101 if (entry.finishTimeNs && BigInt(entry.finishTimeNs.toString()) !== 0n) { 102 const unixNs = BigInt(entry.finishTimeNs.toString()) + this.realToElapsedTimeOffsetNs; 103 finishTime = CrossPlatform.timestamp.fromString( 104 entry.finishTimeNs.toString(), 105 null, 106 unixNs.toString() 107 ); 108 } 109 110 let startTransactionId = null; 111 if (entry.startTransactionId && BigInt(entry.startTransactionId.toString()) !== 0n) { 112 startTransactionId = BigInt(entry.startTransactionId.toString()); 113 } 114 115 let finishTransactionId = null; 116 if (entry.finishTransactionId && BigInt(entry.finishTransactionId.toString()) !== 0n) { 117 finishTransactionId = BigInt(entry.finishTransactionId.toString()); 118 } 119 120 let type = null; 121 if (entry.type !== 0) { 122 type = TransitionType.Companion.fromInt(entry.type); 123 } 124 125 return new Transition( 126 entry.id, 127 new WmTransitionData( 128 createTime, 129 sendTime, 130 abortTime, 131 finishTime, 132 startTransactionId?.toString(), 133 finishTransactionId?.toString(), 134 type, 135 changes 136 ), 137 new ShellTransitionData() 138 ); 139 } 140 141 private validateWmTransitionEntry(entry: any) { 142 if (entry.id === 0) { 143 throw new Error('Entry need a non null id'); 144 } 145 if (!entry.createTimeNs && !entry.sendTimeNs && !entry.abortTimeNs && !entry.finishTimeNs) { 146 throw new Error('Requires at least one non-null timestamp'); 147 } 148 } 149} 150