1*90c8c64dSAndroid Build Coastguard Worker/* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2022 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker * 4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker * 8*90c8c64dSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker * 10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License. 15*90c8c64dSAndroid Build Coastguard Worker */ 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Workerimport {TimeRange, Timestamp} from 'common/time'; 18*90c8c64dSAndroid Build Coastguard Workerimport {ComponentTimestampConverter} from 'common/timestamp_converter'; 19*90c8c64dSAndroid Build Coastguard Workerimport {UserNotifier} from 'common/user_notifier'; 20*90c8c64dSAndroid Build Coastguard Workerimport {CannotParseAllTransitions} from 'messaging/user_warnings'; 21*90c8c64dSAndroid Build Coastguard Workerimport {ScreenRecordingUtils} from 'trace/screen_recording_utils'; 22*90c8c64dSAndroid Build Coastguard Workerimport {Trace, TraceEntry} from 'trace/trace'; 23*90c8c64dSAndroid Build Coastguard Workerimport {Traces} from 'trace/traces'; 24*90c8c64dSAndroid Build Coastguard Workerimport {TraceEntryFinder} from 'trace/trace_entry_finder'; 25*90c8c64dSAndroid Build Coastguard Workerimport {TracePosition} from 'trace/trace_position'; 26*90c8c64dSAndroid Build Coastguard Workerimport {TraceType, TraceTypeUtils} from 'trace/trace_type'; 27*90c8c64dSAndroid Build Coastguard Workerimport {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 28*90c8c64dSAndroid Build Coastguard Worker 29*90c8c64dSAndroid Build Coastguard Workerexport class TimelineData { 30*90c8c64dSAndroid Build Coastguard Worker private traces = new Traces(); 31*90c8c64dSAndroid Build Coastguard Worker private screenRecordingVideo?: Blob; 32*90c8c64dSAndroid Build Coastguard Worker private firstEntry?: TraceEntry<object>; 33*90c8c64dSAndroid Build Coastguard Worker private lastEntry?: TraceEntry<object>; 34*90c8c64dSAndroid Build Coastguard Worker private explicitlySetPosition?: TracePosition; 35*90c8c64dSAndroid Build Coastguard Worker private explicitlySetSelection?: TimeRange; 36*90c8c64dSAndroid Build Coastguard Worker private explicitlySetZoomRange?: TimeRange; 37*90c8c64dSAndroid Build Coastguard Worker private lastReturnedCurrentPosition?: TracePosition; 38*90c8c64dSAndroid Build Coastguard Worker private lastReturnedFullTimeRange?: TimeRange; 39*90c8c64dSAndroid Build Coastguard Worker private lastReturnedCurrentEntries = new Map< 40*90c8c64dSAndroid Build Coastguard Worker Trace<object>, 41*90c8c64dSAndroid Build Coastguard Worker TraceEntry<any> | undefined 42*90c8c64dSAndroid Build Coastguard Worker >(); 43*90c8c64dSAndroid Build Coastguard Worker private activeTrace: Trace<object> | undefined; 44*90c8c64dSAndroid Build Coastguard Worker private transitionEntries: Array<PropertyTreeNode | undefined> = []; // cached trace entries to avoid TP and object creation latencies each time transition timeline is redrawn 45*90c8c64dSAndroid Build Coastguard Worker private timestampConverter: ComponentTimestampConverter | undefined; 46*90c8c64dSAndroid Build Coastguard Worker 47*90c8c64dSAndroid Build Coastguard Worker async initialize( 48*90c8c64dSAndroid Build Coastguard Worker traces: Traces, 49*90c8c64dSAndroid Build Coastguard Worker screenRecordingVideo: Blob | undefined, 50*90c8c64dSAndroid Build Coastguard Worker timestampConverter: ComponentTimestampConverter, 51*90c8c64dSAndroid Build Coastguard Worker ) { 52*90c8c64dSAndroid Build Coastguard Worker this.clear(); 53*90c8c64dSAndroid Build Coastguard Worker 54*90c8c64dSAndroid Build Coastguard Worker this.timestampConverter = timestampConverter; 55*90c8c64dSAndroid Build Coastguard Worker 56*90c8c64dSAndroid Build Coastguard Worker this.traces = new Traces(); 57*90c8c64dSAndroid Build Coastguard Worker traces.forEachTrace((trace, type) => { 58*90c8c64dSAndroid Build Coastguard Worker // Filter out empty traces or dumps with invalid timestamp (would mess up the timeline) 59*90c8c64dSAndroid Build Coastguard Worker if (trace.lengthEntries === 0 || trace.isDumpWithoutTimestamp()) { 60*90c8c64dSAndroid Build Coastguard Worker return; 61*90c8c64dSAndroid Build Coastguard Worker } 62*90c8c64dSAndroid Build Coastguard Worker 63*90c8c64dSAndroid Build Coastguard Worker this.traces.addTrace(trace); 64*90c8c64dSAndroid Build Coastguard Worker }); 65*90c8c64dSAndroid Build Coastguard Worker 66*90c8c64dSAndroid Build Coastguard Worker const transitionTrace = this.traces.getTrace(TraceType.TRANSITION); 67*90c8c64dSAndroid Build Coastguard Worker if (transitionTrace) { 68*90c8c64dSAndroid Build Coastguard Worker let someCorrupted = false; 69*90c8c64dSAndroid Build Coastguard Worker await Promise.all( 70*90c8c64dSAndroid Build Coastguard Worker transitionTrace.mapEntry(async (entry) => { 71*90c8c64dSAndroid Build Coastguard Worker let transition: PropertyTreeNode | undefined; 72*90c8c64dSAndroid Build Coastguard Worker try { 73*90c8c64dSAndroid Build Coastguard Worker transition = await entry.getValue(); 74*90c8c64dSAndroid Build Coastguard Worker } catch (e) { 75*90c8c64dSAndroid Build Coastguard Worker someCorrupted = true; 76*90c8c64dSAndroid Build Coastguard Worker } 77*90c8c64dSAndroid Build Coastguard Worker this.transitionEntries.push(transition); 78*90c8c64dSAndroid Build Coastguard Worker }), 79*90c8c64dSAndroid Build Coastguard Worker ); 80*90c8c64dSAndroid Build Coastguard Worker if (someCorrupted) { 81*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new CannotParseAllTransitions()); 82*90c8c64dSAndroid Build Coastguard Worker } 83*90c8c64dSAndroid Build Coastguard Worker } 84*90c8c64dSAndroid Build Coastguard Worker 85*90c8c64dSAndroid Build Coastguard Worker this.screenRecordingVideo = screenRecordingVideo; 86*90c8c64dSAndroid Build Coastguard Worker this.firstEntry = this.findFirstEntry(); 87*90c8c64dSAndroid Build Coastguard Worker this.lastEntry = this.findLastEntry(); 88*90c8c64dSAndroid Build Coastguard Worker 89*90c8c64dSAndroid Build Coastguard Worker const tracesSortedByDisplayOrder = traces 90*90c8c64dSAndroid Build Coastguard Worker .mapTrace((trace) => trace) 91*90c8c64dSAndroid Build Coastguard Worker .filter((trace) => TraceTypeUtils.isTraceTypeWithViewer(trace.type)) 92*90c8c64dSAndroid Build Coastguard Worker .sort((a, b) => { 93*90c8c64dSAndroid Build Coastguard Worker // do not set screen recording as active unless it is the only trace 94*90c8c64dSAndroid Build Coastguard Worker if (a.type === TraceType.SCREEN_RECORDING) return 1; 95*90c8c64dSAndroid Build Coastguard Worker if (b.type === TraceType.SCREEN_RECORDING) return -1; 96*90c8c64dSAndroid Build Coastguard Worker return TraceTypeUtils.compareByDisplayOrder(a.type, b.type); 97*90c8c64dSAndroid Build Coastguard Worker }); 98*90c8c64dSAndroid Build Coastguard Worker if (tracesSortedByDisplayOrder.length > 0) { 99*90c8c64dSAndroid Build Coastguard Worker this.trySetActiveTrace(tracesSortedByDisplayOrder[0]); 100*90c8c64dSAndroid Build Coastguard Worker } 101*90c8c64dSAndroid Build Coastguard Worker } 102*90c8c64dSAndroid Build Coastguard Worker 103*90c8c64dSAndroid Build Coastguard Worker getTransitionEntries(): Array<PropertyTreeNode | undefined> { 104*90c8c64dSAndroid Build Coastguard Worker return this.transitionEntries; 105*90c8c64dSAndroid Build Coastguard Worker } 106*90c8c64dSAndroid Build Coastguard Worker 107*90c8c64dSAndroid Build Coastguard Worker getTimestampConverter(): ComponentTimestampConverter | undefined { 108*90c8c64dSAndroid Build Coastguard Worker return this.timestampConverter; 109*90c8c64dSAndroid Build Coastguard Worker } 110*90c8c64dSAndroid Build Coastguard Worker 111*90c8c64dSAndroid Build Coastguard Worker getCurrentPosition(): TracePosition | undefined { 112*90c8c64dSAndroid Build Coastguard Worker if (this.explicitlySetPosition) { 113*90c8c64dSAndroid Build Coastguard Worker return this.explicitlySetPosition; 114*90c8c64dSAndroid Build Coastguard Worker } 115*90c8c64dSAndroid Build Coastguard Worker 116*90c8c64dSAndroid Build Coastguard Worker let currentPosition: TracePosition | undefined = undefined; 117*90c8c64dSAndroid Build Coastguard Worker if (this.firstEntry) { 118*90c8c64dSAndroid Build Coastguard Worker currentPosition = TracePosition.fromTraceEntry(this.firstEntry); 119*90c8c64dSAndroid Build Coastguard Worker } 120*90c8c64dSAndroid Build Coastguard Worker 121*90c8c64dSAndroid Build Coastguard Worker const firstActiveEntry = this.getFirstEntryOfActiveViewTrace(); 122*90c8c64dSAndroid Build Coastguard Worker if (firstActiveEntry) { 123*90c8c64dSAndroid Build Coastguard Worker currentPosition = TracePosition.fromTraceEntry(firstActiveEntry); 124*90c8c64dSAndroid Build Coastguard Worker } 125*90c8c64dSAndroid Build Coastguard Worker 126*90c8c64dSAndroid Build Coastguard Worker if ( 127*90c8c64dSAndroid Build Coastguard Worker this.lastReturnedCurrentPosition === undefined || 128*90c8c64dSAndroid Build Coastguard Worker currentPosition === undefined || 129*90c8c64dSAndroid Build Coastguard Worker !this.lastReturnedCurrentPosition.isEqual(currentPosition) 130*90c8c64dSAndroid Build Coastguard Worker ) { 131*90c8c64dSAndroid Build Coastguard Worker this.lastReturnedCurrentPosition = currentPosition; 132*90c8c64dSAndroid Build Coastguard Worker } 133*90c8c64dSAndroid Build Coastguard Worker 134*90c8c64dSAndroid Build Coastguard Worker return this.lastReturnedCurrentPosition; 135*90c8c64dSAndroid Build Coastguard Worker } 136*90c8c64dSAndroid Build Coastguard Worker 137*90c8c64dSAndroid Build Coastguard Worker setPosition(position: TracePosition | undefined) { 138*90c8c64dSAndroid Build Coastguard Worker if (!this.hasTimestamps()) { 139*90c8c64dSAndroid Build Coastguard Worker console.warn( 140*90c8c64dSAndroid Build Coastguard Worker 'Attempted to set position on traces with no timestamps/entries...', 141*90c8c64dSAndroid Build Coastguard Worker ); 142*90c8c64dSAndroid Build Coastguard Worker return; 143*90c8c64dSAndroid Build Coastguard Worker } 144*90c8c64dSAndroid Build Coastguard Worker 145*90c8c64dSAndroid Build Coastguard Worker if (this.firstEntry && position) { 146*90c8c64dSAndroid Build Coastguard Worker if ( 147*90c8c64dSAndroid Build Coastguard Worker this.firstEntry.getTimestamp().getValueNs() > 148*90c8c64dSAndroid Build Coastguard Worker position.timestamp.getValueNs() 149*90c8c64dSAndroid Build Coastguard Worker ) { 150*90c8c64dSAndroid Build Coastguard Worker this.explicitlySetPosition = TracePosition.fromTraceEntry( 151*90c8c64dSAndroid Build Coastguard Worker this.firstEntry, 152*90c8c64dSAndroid Build Coastguard Worker ); 153*90c8c64dSAndroid Build Coastguard Worker return; 154*90c8c64dSAndroid Build Coastguard Worker } 155*90c8c64dSAndroid Build Coastguard Worker } 156*90c8c64dSAndroid Build Coastguard Worker 157*90c8c64dSAndroid Build Coastguard Worker if (this.lastEntry && position) { 158*90c8c64dSAndroid Build Coastguard Worker if ( 159*90c8c64dSAndroid Build Coastguard Worker this.lastEntry.getTimestamp().getValueNs() < 160*90c8c64dSAndroid Build Coastguard Worker position.timestamp.getValueNs() 161*90c8c64dSAndroid Build Coastguard Worker ) { 162*90c8c64dSAndroid Build Coastguard Worker this.explicitlySetPosition = TracePosition.fromTraceEntry( 163*90c8c64dSAndroid Build Coastguard Worker this.lastEntry, 164*90c8c64dSAndroid Build Coastguard Worker ); 165*90c8c64dSAndroid Build Coastguard Worker return; 166*90c8c64dSAndroid Build Coastguard Worker } 167*90c8c64dSAndroid Build Coastguard Worker } 168*90c8c64dSAndroid Build Coastguard Worker 169*90c8c64dSAndroid Build Coastguard Worker this.explicitlySetPosition = position; 170*90c8c64dSAndroid Build Coastguard Worker } 171*90c8c64dSAndroid Build Coastguard Worker 172*90c8c64dSAndroid Build Coastguard Worker makePositionFromActiveTrace(timestamp: Timestamp): TracePosition { 173*90c8c64dSAndroid Build Coastguard Worker if (!this.activeTrace) { 174*90c8c64dSAndroid Build Coastguard Worker return TracePosition.fromTimestamp(timestamp); 175*90c8c64dSAndroid Build Coastguard Worker } 176*90c8c64dSAndroid Build Coastguard Worker 177*90c8c64dSAndroid Build Coastguard Worker const entry = this.activeTrace.findClosestEntry(timestamp); 178*90c8c64dSAndroid Build Coastguard Worker if (!entry) { 179*90c8c64dSAndroid Build Coastguard Worker return TracePosition.fromTimestamp(timestamp); 180*90c8c64dSAndroid Build Coastguard Worker } 181*90c8c64dSAndroid Build Coastguard Worker 182*90c8c64dSAndroid Build Coastguard Worker return TracePosition.fromTraceEntry(entry, timestamp); 183*90c8c64dSAndroid Build Coastguard Worker } 184*90c8c64dSAndroid Build Coastguard Worker 185*90c8c64dSAndroid Build Coastguard Worker trySetActiveTrace(trace: Trace<object>): boolean { 186*90c8c64dSAndroid Build Coastguard Worker const isTraceWithValidTimestamps = this.traces.hasTrace(trace); 187*90c8c64dSAndroid Build Coastguard Worker if (this.activeTrace !== trace && isTraceWithValidTimestamps) { 188*90c8c64dSAndroid Build Coastguard Worker this.activeTrace = trace; 189*90c8c64dSAndroid Build Coastguard Worker return true; 190*90c8c64dSAndroid Build Coastguard Worker } 191*90c8c64dSAndroid Build Coastguard Worker return false; 192*90c8c64dSAndroid Build Coastguard Worker } 193*90c8c64dSAndroid Build Coastguard Worker 194*90c8c64dSAndroid Build Coastguard Worker getActiveTrace() { 195*90c8c64dSAndroid Build Coastguard Worker return this.activeTrace; 196*90c8c64dSAndroid Build Coastguard Worker } 197*90c8c64dSAndroid Build Coastguard Worker 198*90c8c64dSAndroid Build Coastguard Worker getFullTimeRange(): TimeRange { 199*90c8c64dSAndroid Build Coastguard Worker if (!this.firstEntry || !this.lastEntry) { 200*90c8c64dSAndroid Build Coastguard Worker throw new Error( 201*90c8c64dSAndroid Build Coastguard Worker 'Trying to get full time range when there are no timestamps', 202*90c8c64dSAndroid Build Coastguard Worker ); 203*90c8c64dSAndroid Build Coastguard Worker } 204*90c8c64dSAndroid Build Coastguard Worker 205*90c8c64dSAndroid Build Coastguard Worker const fullTimeRange = new TimeRange( 206*90c8c64dSAndroid Build Coastguard Worker this.firstEntry.getTimestamp(), 207*90c8c64dSAndroid Build Coastguard Worker this.lastEntry.getTimestamp(), 208*90c8c64dSAndroid Build Coastguard Worker ); 209*90c8c64dSAndroid Build Coastguard Worker 210*90c8c64dSAndroid Build Coastguard Worker if ( 211*90c8c64dSAndroid Build Coastguard Worker this.lastReturnedFullTimeRange === undefined || 212*90c8c64dSAndroid Build Coastguard Worker this.lastReturnedFullTimeRange.from.getValueNs() !== 213*90c8c64dSAndroid Build Coastguard Worker fullTimeRange.from.getValueNs() || 214*90c8c64dSAndroid Build Coastguard Worker this.lastReturnedFullTimeRange.to.getValueNs() !== 215*90c8c64dSAndroid Build Coastguard Worker fullTimeRange.to.getValueNs() 216*90c8c64dSAndroid Build Coastguard Worker ) { 217*90c8c64dSAndroid Build Coastguard Worker this.lastReturnedFullTimeRange = fullTimeRange; 218*90c8c64dSAndroid Build Coastguard Worker } 219*90c8c64dSAndroid Build Coastguard Worker 220*90c8c64dSAndroid Build Coastguard Worker return this.lastReturnedFullTimeRange; 221*90c8c64dSAndroid Build Coastguard Worker } 222*90c8c64dSAndroid Build Coastguard Worker 223*90c8c64dSAndroid Build Coastguard Worker getSelectionTimeRange(): TimeRange { 224*90c8c64dSAndroid Build Coastguard Worker if (this.explicitlySetSelection === undefined) { 225*90c8c64dSAndroid Build Coastguard Worker return this.getFullTimeRange(); 226*90c8c64dSAndroid Build Coastguard Worker } else { 227*90c8c64dSAndroid Build Coastguard Worker return this.explicitlySetSelection; 228*90c8c64dSAndroid Build Coastguard Worker } 229*90c8c64dSAndroid Build Coastguard Worker } 230*90c8c64dSAndroid Build Coastguard Worker 231*90c8c64dSAndroid Build Coastguard Worker setSelectionTimeRange(selection: TimeRange) { 232*90c8c64dSAndroid Build Coastguard Worker this.explicitlySetSelection = selection; 233*90c8c64dSAndroid Build Coastguard Worker } 234*90c8c64dSAndroid Build Coastguard Worker 235*90c8c64dSAndroid Build Coastguard Worker getZoomRange(): TimeRange { 236*90c8c64dSAndroid Build Coastguard Worker if (this.explicitlySetZoomRange === undefined) { 237*90c8c64dSAndroid Build Coastguard Worker return this.getFullTimeRange(); 238*90c8c64dSAndroid Build Coastguard Worker } else { 239*90c8c64dSAndroid Build Coastguard Worker return this.explicitlySetZoomRange; 240*90c8c64dSAndroid Build Coastguard Worker } 241*90c8c64dSAndroid Build Coastguard Worker } 242*90c8c64dSAndroid Build Coastguard Worker 243*90c8c64dSAndroid Build Coastguard Worker setZoom(zoomRange: TimeRange) { 244*90c8c64dSAndroid Build Coastguard Worker this.explicitlySetZoomRange = zoomRange; 245*90c8c64dSAndroid Build Coastguard Worker } 246*90c8c64dSAndroid Build Coastguard Worker 247*90c8c64dSAndroid Build Coastguard Worker getTraces(): Traces { 248*90c8c64dSAndroid Build Coastguard Worker return this.traces; 249*90c8c64dSAndroid Build Coastguard Worker } 250*90c8c64dSAndroid Build Coastguard Worker 251*90c8c64dSAndroid Build Coastguard Worker hasTrace(trace: Trace<object>): boolean { 252*90c8c64dSAndroid Build Coastguard Worker return this.traces.hasTrace(trace); 253*90c8c64dSAndroid Build Coastguard Worker } 254*90c8c64dSAndroid Build Coastguard Worker 255*90c8c64dSAndroid Build Coastguard Worker getScreenRecordingVideo(): Blob | undefined { 256*90c8c64dSAndroid Build Coastguard Worker return this.screenRecordingVideo; 257*90c8c64dSAndroid Build Coastguard Worker } 258*90c8c64dSAndroid Build Coastguard Worker 259*90c8c64dSAndroid Build Coastguard Worker searchCorrespondingScreenRecordingTimeSeconds( 260*90c8c64dSAndroid Build Coastguard Worker position: TracePosition, 261*90c8c64dSAndroid Build Coastguard Worker ): number | undefined { 262*90c8c64dSAndroid Build Coastguard Worker const trace = this.traces.getTrace(TraceType.SCREEN_RECORDING); 263*90c8c64dSAndroid Build Coastguard Worker if (!trace) { 264*90c8c64dSAndroid Build Coastguard Worker return undefined; 265*90c8c64dSAndroid Build Coastguard Worker } 266*90c8c64dSAndroid Build Coastguard Worker 267*90c8c64dSAndroid Build Coastguard Worker const firstTimestamp = trace.getEntry(0).getTimestamp(); 268*90c8c64dSAndroid Build Coastguard Worker const entry = TraceEntryFinder.findCorrespondingEntry(trace, position); 269*90c8c64dSAndroid Build Coastguard Worker if (!entry) { 270*90c8c64dSAndroid Build Coastguard Worker return undefined; 271*90c8c64dSAndroid Build Coastguard Worker } 272*90c8c64dSAndroid Build Coastguard Worker 273*90c8c64dSAndroid Build Coastguard Worker return ScreenRecordingUtils.timestampToVideoTimeSeconds( 274*90c8c64dSAndroid Build Coastguard Worker firstTimestamp.getValueNs(), 275*90c8c64dSAndroid Build Coastguard Worker entry.getTimestamp().getValueNs(), 276*90c8c64dSAndroid Build Coastguard Worker ); 277*90c8c64dSAndroid Build Coastguard Worker } 278*90c8c64dSAndroid Build Coastguard Worker 279*90c8c64dSAndroid Build Coastguard Worker hasTimestamps(): boolean { 280*90c8c64dSAndroid Build Coastguard Worker return this.firstEntry !== undefined; 281*90c8c64dSAndroid Build Coastguard Worker } 282*90c8c64dSAndroid Build Coastguard Worker 283*90c8c64dSAndroid Build Coastguard Worker hasMoreThanOneDistinctTimestamp(): boolean { 284*90c8c64dSAndroid Build Coastguard Worker return ( 285*90c8c64dSAndroid Build Coastguard Worker this.hasTimestamps() && 286*90c8c64dSAndroid Build Coastguard Worker this.firstEntry?.getTimestamp().getValueNs() !== 287*90c8c64dSAndroid Build Coastguard Worker this.lastEntry?.getTimestamp().getValueNs() 288*90c8c64dSAndroid Build Coastguard Worker ); 289*90c8c64dSAndroid Build Coastguard Worker } 290*90c8c64dSAndroid Build Coastguard Worker 291*90c8c64dSAndroid Build Coastguard Worker getPreviousEntryFor(trace: Trace<object>): TraceEntry<object> | undefined { 292*90c8c64dSAndroid Build Coastguard Worker if (trace.lengthEntries === 0) { 293*90c8c64dSAndroid Build Coastguard Worker return undefined; 294*90c8c64dSAndroid Build Coastguard Worker } 295*90c8c64dSAndroid Build Coastguard Worker 296*90c8c64dSAndroid Build Coastguard Worker const currentIndex = this.findCurrentEntryFor(trace)?.getIndex(); 297*90c8c64dSAndroid Build Coastguard Worker if (currentIndex === undefined || currentIndex === 0) { 298*90c8c64dSAndroid Build Coastguard Worker return undefined; 299*90c8c64dSAndroid Build Coastguard Worker } 300*90c8c64dSAndroid Build Coastguard Worker 301*90c8c64dSAndroid Build Coastguard Worker return trace.getEntry(currentIndex - 1); 302*90c8c64dSAndroid Build Coastguard Worker } 303*90c8c64dSAndroid Build Coastguard Worker 304*90c8c64dSAndroid Build Coastguard Worker getNextEntryFor(trace: Trace<object>): TraceEntry<object> | undefined { 305*90c8c64dSAndroid Build Coastguard Worker if (trace.lengthEntries === 0) { 306*90c8c64dSAndroid Build Coastguard Worker return undefined; 307*90c8c64dSAndroid Build Coastguard Worker } 308*90c8c64dSAndroid Build Coastguard Worker 309*90c8c64dSAndroid Build Coastguard Worker const currentIndex = this.findCurrentEntryFor(trace)?.getIndex(); 310*90c8c64dSAndroid Build Coastguard Worker if (currentIndex === undefined) { 311*90c8c64dSAndroid Build Coastguard Worker return trace.getEntry(0); 312*90c8c64dSAndroid Build Coastguard Worker } 313*90c8c64dSAndroid Build Coastguard Worker 314*90c8c64dSAndroid Build Coastguard Worker if (currentIndex + 1 >= trace.lengthEntries) { 315*90c8c64dSAndroid Build Coastguard Worker return undefined; 316*90c8c64dSAndroid Build Coastguard Worker } 317*90c8c64dSAndroid Build Coastguard Worker 318*90c8c64dSAndroid Build Coastguard Worker return trace.getEntry(currentIndex + 1); 319*90c8c64dSAndroid Build Coastguard Worker } 320*90c8c64dSAndroid Build Coastguard Worker 321*90c8c64dSAndroid Build Coastguard Worker findCurrentEntryFor(trace: Trace<object>): TraceEntry<object> | undefined { 322*90c8c64dSAndroid Build Coastguard Worker const position = this.getCurrentPosition(); 323*90c8c64dSAndroid Build Coastguard Worker if (!position) { 324*90c8c64dSAndroid Build Coastguard Worker return undefined; 325*90c8c64dSAndroid Build Coastguard Worker } 326*90c8c64dSAndroid Build Coastguard Worker 327*90c8c64dSAndroid Build Coastguard Worker const entry = TraceEntryFinder.findCorrespondingEntry(trace, position); 328*90c8c64dSAndroid Build Coastguard Worker 329*90c8c64dSAndroid Build Coastguard Worker if ( 330*90c8c64dSAndroid Build Coastguard Worker this.lastReturnedCurrentEntries.get(trace)?.getIndex() !== 331*90c8c64dSAndroid Build Coastguard Worker entry?.getIndex() 332*90c8c64dSAndroid Build Coastguard Worker ) { 333*90c8c64dSAndroid Build Coastguard Worker this.lastReturnedCurrentEntries.set(trace, entry); 334*90c8c64dSAndroid Build Coastguard Worker } 335*90c8c64dSAndroid Build Coastguard Worker 336*90c8c64dSAndroid Build Coastguard Worker return this.lastReturnedCurrentEntries.get(trace); 337*90c8c64dSAndroid Build Coastguard Worker } 338*90c8c64dSAndroid Build Coastguard Worker 339*90c8c64dSAndroid Build Coastguard Worker moveToPreviousEntryFor(trace: Trace<object>) { 340*90c8c64dSAndroid Build Coastguard Worker const prevEntry = this.getPreviousEntryFor(trace); 341*90c8c64dSAndroid Build Coastguard Worker if (prevEntry !== undefined) { 342*90c8c64dSAndroid Build Coastguard Worker this.setPosition(TracePosition.fromTraceEntry(prevEntry)); 343*90c8c64dSAndroid Build Coastguard Worker } 344*90c8c64dSAndroid Build Coastguard Worker } 345*90c8c64dSAndroid Build Coastguard Worker 346*90c8c64dSAndroid Build Coastguard Worker moveToNextEntryFor(trace: Trace<object>) { 347*90c8c64dSAndroid Build Coastguard Worker const nextEntry = this.getNextEntryFor(trace); 348*90c8c64dSAndroid Build Coastguard Worker if (nextEntry !== undefined) { 349*90c8c64dSAndroid Build Coastguard Worker this.setPosition(TracePosition.fromTraceEntry(nextEntry)); 350*90c8c64dSAndroid Build Coastguard Worker } 351*90c8c64dSAndroid Build Coastguard Worker } 352*90c8c64dSAndroid Build Coastguard Worker 353*90c8c64dSAndroid Build Coastguard Worker clear() { 354*90c8c64dSAndroid Build Coastguard Worker this.traces = new Traces(); 355*90c8c64dSAndroid Build Coastguard Worker this.firstEntry = undefined; 356*90c8c64dSAndroid Build Coastguard Worker this.lastEntry = undefined; 357*90c8c64dSAndroid Build Coastguard Worker this.explicitlySetPosition = undefined; 358*90c8c64dSAndroid Build Coastguard Worker this.explicitlySetSelection = undefined; 359*90c8c64dSAndroid Build Coastguard Worker this.lastReturnedCurrentPosition = undefined; 360*90c8c64dSAndroid Build Coastguard Worker this.screenRecordingVideo = undefined; 361*90c8c64dSAndroid Build Coastguard Worker this.lastReturnedFullTimeRange = undefined; 362*90c8c64dSAndroid Build Coastguard Worker this.lastReturnedCurrentEntries.clear(); 363*90c8c64dSAndroid Build Coastguard Worker this.activeTrace = undefined; 364*90c8c64dSAndroid Build Coastguard Worker } 365*90c8c64dSAndroid Build Coastguard Worker 366*90c8c64dSAndroid Build Coastguard Worker private findFirstEntry(): TraceEntry<{}> | undefined { 367*90c8c64dSAndroid Build Coastguard Worker let first: TraceEntry<{}> | undefined; 368*90c8c64dSAndroid Build Coastguard Worker 369*90c8c64dSAndroid Build Coastguard Worker this.traces.forEachTrace((trace) => { 370*90c8c64dSAndroid Build Coastguard Worker let candidate: TraceEntry<{}> | undefined; 371*90c8c64dSAndroid Build Coastguard Worker for (let i = 0; i < trace.lengthEntries; i++) { 372*90c8c64dSAndroid Build Coastguard Worker const entry = trace.getEntry(i); 373*90c8c64dSAndroid Build Coastguard Worker if (entry.hasValidTimestamp()) { 374*90c8c64dSAndroid Build Coastguard Worker candidate = entry; 375*90c8c64dSAndroid Build Coastguard Worker break; 376*90c8c64dSAndroid Build Coastguard Worker } 377*90c8c64dSAndroid Build Coastguard Worker } 378*90c8c64dSAndroid Build Coastguard Worker if ( 379*90c8c64dSAndroid Build Coastguard Worker candidate && 380*90c8c64dSAndroid Build Coastguard Worker (!first || candidate.getTimestamp() < first.getTimestamp()) 381*90c8c64dSAndroid Build Coastguard Worker ) { 382*90c8c64dSAndroid Build Coastguard Worker first = candidate; 383*90c8c64dSAndroid Build Coastguard Worker } 384*90c8c64dSAndroid Build Coastguard Worker }); 385*90c8c64dSAndroid Build Coastguard Worker 386*90c8c64dSAndroid Build Coastguard Worker return first; 387*90c8c64dSAndroid Build Coastguard Worker } 388*90c8c64dSAndroid Build Coastguard Worker 389*90c8c64dSAndroid Build Coastguard Worker private findLastEntry(): TraceEntry<{}> | undefined { 390*90c8c64dSAndroid Build Coastguard Worker let last: TraceEntry<{}> | undefined = undefined; 391*90c8c64dSAndroid Build Coastguard Worker 392*90c8c64dSAndroid Build Coastguard Worker this.traces.forEachTrace((trace) => { 393*90c8c64dSAndroid Build Coastguard Worker const candidate = trace.getEntry(trace.lengthEntries - 1); 394*90c8c64dSAndroid Build Coastguard Worker if (!last || candidate.getTimestamp() > last.getTimestamp()) { 395*90c8c64dSAndroid Build Coastguard Worker last = candidate; 396*90c8c64dSAndroid Build Coastguard Worker } 397*90c8c64dSAndroid Build Coastguard Worker }); 398*90c8c64dSAndroid Build Coastguard Worker 399*90c8c64dSAndroid Build Coastguard Worker return last; 400*90c8c64dSAndroid Build Coastguard Worker } 401*90c8c64dSAndroid Build Coastguard Worker 402*90c8c64dSAndroid Build Coastguard Worker private getFirstEntryOfActiveViewTrace(): TraceEntry<{}> | undefined { 403*90c8c64dSAndroid Build Coastguard Worker if (!this.activeTrace) { 404*90c8c64dSAndroid Build Coastguard Worker return undefined; 405*90c8c64dSAndroid Build Coastguard Worker } 406*90c8c64dSAndroid Build Coastguard Worker return this.activeTrace.getEntry(0); 407*90c8c64dSAndroid Build Coastguard Worker } 408*90c8c64dSAndroid Build Coastguard Worker} 409