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 {FileUtils} from 'common/file_utils'; 18*90c8c64dSAndroid Build Coastguard Workerimport {OnProgressUpdateType} from 'common/function_utils'; 19*90c8c64dSAndroid Build Coastguard Workerimport { 20*90c8c64dSAndroid Build Coastguard Worker TimestampConverter, 21*90c8c64dSAndroid Build Coastguard Worker UTC_TIMEZONE_INFO, 22*90c8c64dSAndroid Build Coastguard Worker} from 'common/timestamp_converter'; 23*90c8c64dSAndroid Build Coastguard Workerimport {UserNotifier} from 'common/user_notifier'; 24*90c8c64dSAndroid Build Coastguard Workerimport {Analytics} from 'logging/analytics'; 25*90c8c64dSAndroid Build Coastguard Workerimport {ProgressListener} from 'messaging/progress_listener'; 26*90c8c64dSAndroid Build Coastguard Workerimport {CorruptedArchive, NoValidFiles} from 'messaging/user_warnings'; 27*90c8c64dSAndroid Build Coastguard Workerimport {FileAndParsers} from 'parsers/file_and_parsers'; 28*90c8c64dSAndroid Build Coastguard Workerimport {ParserFactory as LegacyParserFactory} from 'parsers/legacy/parser_factory'; 29*90c8c64dSAndroid Build Coastguard Workerimport {ParserFactory as PerfettoParserFactory} from 'parsers/perfetto/parser_factory'; 30*90c8c64dSAndroid Build Coastguard Workerimport {ParserSearch} from 'parsers/search/parser_search'; 31*90c8c64dSAndroid Build Coastguard Workerimport {TracesParserFactory} from 'parsers/traces/traces_parser_factory'; 32*90c8c64dSAndroid Build Coastguard Workerimport {FrameMapper} from 'trace/frame_mapper'; 33*90c8c64dSAndroid Build Coastguard Workerimport {Trace} from 'trace/trace'; 34*90c8c64dSAndroid Build Coastguard Workerimport {Traces} from 'trace/traces'; 35*90c8c64dSAndroid Build Coastguard Workerimport {TraceFile} from 'trace/trace_file'; 36*90c8c64dSAndroid Build Coastguard Workerimport {TraceEntryTypeMap, TraceType, TraceTypeUtils} from 'trace/trace_type'; 37*90c8c64dSAndroid Build Coastguard Workerimport {QueryResult} from 'trace_processor/query_result'; 38*90c8c64dSAndroid Build Coastguard Workerimport {FilesSource} from './files_source'; 39*90c8c64dSAndroid Build Coastguard Workerimport {LoadedParsers} from './loaded_parsers'; 40*90c8c64dSAndroid Build Coastguard Workerimport {TraceFileFilter} from './trace_file_filter'; 41*90c8c64dSAndroid Build Coastguard Worker 42*90c8c64dSAndroid Build Coastguard Workertype UnzippedArchive = TraceFile[]; 43*90c8c64dSAndroid Build Coastguard Worker 44*90c8c64dSAndroid Build Coastguard Workerexport class TracePipeline { 45*90c8c64dSAndroid Build Coastguard Worker private loadedParsers = new LoadedParsers(); 46*90c8c64dSAndroid Build Coastguard Worker private traceFileFilter = new TraceFileFilter(); 47*90c8c64dSAndroid Build Coastguard Worker private tracesParserFactory = new TracesParserFactory(); 48*90c8c64dSAndroid Build Coastguard Worker private traces = new Traces(); 49*90c8c64dSAndroid Build Coastguard Worker private downloadArchiveFilename?: string; 50*90c8c64dSAndroid Build Coastguard Worker private timestampConverter = new TimestampConverter(UTC_TIMEZONE_INFO); 51*90c8c64dSAndroid Build Coastguard Worker 52*90c8c64dSAndroid Build Coastguard Worker async loadFiles( 53*90c8c64dSAndroid Build Coastguard Worker files: File[], 54*90c8c64dSAndroid Build Coastguard Worker source: FilesSource, 55*90c8c64dSAndroid Build Coastguard Worker progressListener: ProgressListener | undefined, 56*90c8c64dSAndroid Build Coastguard Worker ) { 57*90c8c64dSAndroid Build Coastguard Worker this.downloadArchiveFilename = this.makeDownloadArchiveFilename( 58*90c8c64dSAndroid Build Coastguard Worker files, 59*90c8c64dSAndroid Build Coastguard Worker source, 60*90c8c64dSAndroid Build Coastguard Worker ); 61*90c8c64dSAndroid Build Coastguard Worker 62*90c8c64dSAndroid Build Coastguard Worker try { 63*90c8c64dSAndroid Build Coastguard Worker const unzippedArchives = await this.unzipFiles(files, progressListener); 64*90c8c64dSAndroid Build Coastguard Worker 65*90c8c64dSAndroid Build Coastguard Worker if (unzippedArchives.length === 0) { 66*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new NoValidFiles()); 67*90c8c64dSAndroid Build Coastguard Worker return; 68*90c8c64dSAndroid Build Coastguard Worker } 69*90c8c64dSAndroid Build Coastguard Worker 70*90c8c64dSAndroid Build Coastguard Worker for (const unzippedArchive of unzippedArchives) { 71*90c8c64dSAndroid Build Coastguard Worker await this.loadUnzippedArchive(unzippedArchive, progressListener); 72*90c8c64dSAndroid Build Coastguard Worker } 73*90c8c64dSAndroid Build Coastguard Worker 74*90c8c64dSAndroid Build Coastguard Worker this.traces = new Traces(); 75*90c8c64dSAndroid Build Coastguard Worker 76*90c8c64dSAndroid Build Coastguard Worker this.loadedParsers.getParsers().forEach((parser) => { 77*90c8c64dSAndroid Build Coastguard Worker const trace = Trace.fromParser(parser); 78*90c8c64dSAndroid Build Coastguard Worker this.traces.addTrace(trace); 79*90c8c64dSAndroid Build Coastguard Worker Analytics.Tracing.logTraceLoaded(parser); 80*90c8c64dSAndroid Build Coastguard Worker }); 81*90c8c64dSAndroid Build Coastguard Worker 82*90c8c64dSAndroid Build Coastguard Worker const tracesParsers = await this.tracesParserFactory.createParsers( 83*90c8c64dSAndroid Build Coastguard Worker this.traces, 84*90c8c64dSAndroid Build Coastguard Worker this.timestampConverter, 85*90c8c64dSAndroid Build Coastguard Worker ); 86*90c8c64dSAndroid Build Coastguard Worker 87*90c8c64dSAndroid Build Coastguard Worker tracesParsers.forEach((tracesParser) => { 88*90c8c64dSAndroid Build Coastguard Worker const trace = Trace.fromParser(tracesParser); 89*90c8c64dSAndroid Build Coastguard Worker this.traces.addTrace(trace); 90*90c8c64dSAndroid Build Coastguard Worker }); 91*90c8c64dSAndroid Build Coastguard Worker 92*90c8c64dSAndroid Build Coastguard Worker const hasTransitionTrace = 93*90c8c64dSAndroid Build Coastguard Worker this.traces.getTrace(TraceType.TRANSITION) !== undefined; 94*90c8c64dSAndroid Build Coastguard Worker if (hasTransitionTrace) { 95*90c8c64dSAndroid Build Coastguard Worker this.removeTracesAndParsersByType(TraceType.WM_TRANSITION); 96*90c8c64dSAndroid Build Coastguard Worker this.removeTracesAndParsersByType(TraceType.SHELL_TRANSITION); 97*90c8c64dSAndroid Build Coastguard Worker } 98*90c8c64dSAndroid Build Coastguard Worker 99*90c8c64dSAndroid Build Coastguard Worker const hasCujTrace = this.traces.getTrace(TraceType.CUJS) !== undefined; 100*90c8c64dSAndroid Build Coastguard Worker if (hasCujTrace) { 101*90c8c64dSAndroid Build Coastguard Worker this.removeTracesAndParsersByType(TraceType.EVENT_LOG); 102*90c8c64dSAndroid Build Coastguard Worker } 103*90c8c64dSAndroid Build Coastguard Worker 104*90c8c64dSAndroid Build Coastguard Worker const hasMergedInputTrace = 105*90c8c64dSAndroid Build Coastguard Worker this.traces.getTrace(TraceType.INPUT_EVENT_MERGED) !== undefined; 106*90c8c64dSAndroid Build Coastguard Worker if (hasMergedInputTrace) { 107*90c8c64dSAndroid Build Coastguard Worker this.removeTracesAndParsersByType(TraceType.INPUT_KEY_EVENT); 108*90c8c64dSAndroid Build Coastguard Worker this.removeTracesAndParsersByType(TraceType.INPUT_MOTION_EVENT); 109*90c8c64dSAndroid Build Coastguard Worker } 110*90c8c64dSAndroid Build Coastguard Worker } finally { 111*90c8c64dSAndroid Build Coastguard Worker progressListener?.onOperationFinished(true); 112*90c8c64dSAndroid Build Coastguard Worker } 113*90c8c64dSAndroid Build Coastguard Worker } 114*90c8c64dSAndroid Build Coastguard Worker 115*90c8c64dSAndroid Build Coastguard Worker removeTrace<T extends TraceType>( 116*90c8c64dSAndroid Build Coastguard Worker trace: Trace<TraceEntryTypeMap[T]>, 117*90c8c64dSAndroid Build Coastguard Worker keepFileForDownload = false, 118*90c8c64dSAndroid Build Coastguard Worker ) { 119*90c8c64dSAndroid Build Coastguard Worker this.loadedParsers.remove(trace.getParser(), keepFileForDownload); 120*90c8c64dSAndroid Build Coastguard Worker this.traces.deleteTrace(trace); 121*90c8c64dSAndroid Build Coastguard Worker } 122*90c8c64dSAndroid Build Coastguard Worker 123*90c8c64dSAndroid Build Coastguard Worker async makeZipArchiveWithLoadedTraceFiles( 124*90c8c64dSAndroid Build Coastguard Worker onProgressUpdate?: OnProgressUpdateType, 125*90c8c64dSAndroid Build Coastguard Worker ): Promise<Blob> { 126*90c8c64dSAndroid Build Coastguard Worker return this.loadedParsers.makeZipArchive(onProgressUpdate); 127*90c8c64dSAndroid Build Coastguard Worker } 128*90c8c64dSAndroid Build Coastguard Worker 129*90c8c64dSAndroid Build Coastguard Worker filterTracesWithoutVisualization() { 130*90c8c64dSAndroid Build Coastguard Worker const tracesWithoutVisualization = this.traces 131*90c8c64dSAndroid Build Coastguard Worker .mapTrace((trace) => { 132*90c8c64dSAndroid Build Coastguard Worker if (!TraceTypeUtils.isTraceTypeWithViewer(trace.type)) { 133*90c8c64dSAndroid Build Coastguard Worker return trace; 134*90c8c64dSAndroid Build Coastguard Worker } 135*90c8c64dSAndroid Build Coastguard Worker return undefined; 136*90c8c64dSAndroid Build Coastguard Worker }) 137*90c8c64dSAndroid Build Coastguard Worker .filter((trace) => trace !== undefined) as Array<Trace<object>>; 138*90c8c64dSAndroid Build Coastguard Worker tracesWithoutVisualization.forEach((trace) => 139*90c8c64dSAndroid Build Coastguard Worker this.traces.deleteTrace(trace), 140*90c8c64dSAndroid Build Coastguard Worker ); 141*90c8c64dSAndroid Build Coastguard Worker } 142*90c8c64dSAndroid Build Coastguard Worker 143*90c8c64dSAndroid Build Coastguard Worker async buildTraces() { 144*90c8c64dSAndroid Build Coastguard Worker for (const trace of this.traces) { 145*90c8c64dSAndroid Build Coastguard Worker if (trace.lengthEntries === 0 || trace.isDumpWithoutTimestamp()) { 146*90c8c64dSAndroid Build Coastguard Worker continue; 147*90c8c64dSAndroid Build Coastguard Worker } else { 148*90c8c64dSAndroid Build Coastguard Worker const timestamp = trace.getEntry(0).getTimestamp(); 149*90c8c64dSAndroid Build Coastguard Worker this.timestampConverter.initializeUTCOffset(timestamp); 150*90c8c64dSAndroid Build Coastguard Worker break; 151*90c8c64dSAndroid Build Coastguard Worker } 152*90c8c64dSAndroid Build Coastguard Worker } 153*90c8c64dSAndroid Build Coastguard Worker await new FrameMapper(this.traces).computeMapping(); 154*90c8c64dSAndroid Build Coastguard Worker } 155*90c8c64dSAndroid Build Coastguard Worker 156*90c8c64dSAndroid Build Coastguard Worker getTraces(): Traces { 157*90c8c64dSAndroid Build Coastguard Worker return this.traces; 158*90c8c64dSAndroid Build Coastguard Worker } 159*90c8c64dSAndroid Build Coastguard Worker 160*90c8c64dSAndroid Build Coastguard Worker getDownloadArchiveFilename(): string { 161*90c8c64dSAndroid Build Coastguard Worker return this.downloadArchiveFilename ?? 'winscope'; 162*90c8c64dSAndroid Build Coastguard Worker } 163*90c8c64dSAndroid Build Coastguard Worker 164*90c8c64dSAndroid Build Coastguard Worker getTimestampConverter(): TimestampConverter { 165*90c8c64dSAndroid Build Coastguard Worker return this.timestampConverter; 166*90c8c64dSAndroid Build Coastguard Worker } 167*90c8c64dSAndroid Build Coastguard Worker 168*90c8c64dSAndroid Build Coastguard Worker async getScreenRecordingVideo(): Promise<undefined | Blob> { 169*90c8c64dSAndroid Build Coastguard Worker const traces = this.getTraces(); 170*90c8c64dSAndroid Build Coastguard Worker const screenRecording = 171*90c8c64dSAndroid Build Coastguard Worker traces.getTrace(TraceType.SCREEN_RECORDING) ?? 172*90c8c64dSAndroid Build Coastguard Worker traces.getTrace(TraceType.SCREENSHOT); 173*90c8c64dSAndroid Build Coastguard Worker if (!screenRecording || screenRecording.lengthEntries === 0) { 174*90c8c64dSAndroid Build Coastguard Worker return undefined; 175*90c8c64dSAndroid Build Coastguard Worker } 176*90c8c64dSAndroid Build Coastguard Worker return (await screenRecording.getEntry(0).getValue()).videoData; 177*90c8c64dSAndroid Build Coastguard Worker } 178*90c8c64dSAndroid Build Coastguard Worker 179*90c8c64dSAndroid Build Coastguard Worker async tryCreateSearchTrace( 180*90c8c64dSAndroid Build Coastguard Worker query: string, 181*90c8c64dSAndroid Build Coastguard Worker ): Promise<Trace<QueryResult> | undefined> { 182*90c8c64dSAndroid Build Coastguard Worker try { 183*90c8c64dSAndroid Build Coastguard Worker const parser = new ParserSearch(query, this.timestampConverter); 184*90c8c64dSAndroid Build Coastguard Worker await parser.parse(); 185*90c8c64dSAndroid Build Coastguard Worker const trace = Trace.fromParser(parser); 186*90c8c64dSAndroid Build Coastguard Worker this.traces.addTrace(trace); 187*90c8c64dSAndroid Build Coastguard Worker return trace; 188*90c8c64dSAndroid Build Coastguard Worker } catch (e) { 189*90c8c64dSAndroid Build Coastguard Worker return undefined; 190*90c8c64dSAndroid Build Coastguard Worker } 191*90c8c64dSAndroid Build Coastguard Worker } 192*90c8c64dSAndroid Build Coastguard Worker 193*90c8c64dSAndroid Build Coastguard Worker clear() { 194*90c8c64dSAndroid Build Coastguard Worker this.loadedParsers.clear(); 195*90c8c64dSAndroid Build Coastguard Worker this.traces = new Traces(); 196*90c8c64dSAndroid Build Coastguard Worker this.timestampConverter.clear(); 197*90c8c64dSAndroid Build Coastguard Worker this.downloadArchiveFilename = undefined; 198*90c8c64dSAndroid Build Coastguard Worker } 199*90c8c64dSAndroid Build Coastguard Worker 200*90c8c64dSAndroid Build Coastguard Worker private async loadUnzippedArchive( 201*90c8c64dSAndroid Build Coastguard Worker unzippedArchive: UnzippedArchive, 202*90c8c64dSAndroid Build Coastguard Worker progressListener: ProgressListener | undefined, 203*90c8c64dSAndroid Build Coastguard Worker ) { 204*90c8c64dSAndroid Build Coastguard Worker const filterResult = await this.traceFileFilter.filter(unzippedArchive); 205*90c8c64dSAndroid Build Coastguard Worker if (filterResult.timezoneInfo) { 206*90c8c64dSAndroid Build Coastguard Worker this.timestampConverter = new TimestampConverter( 207*90c8c64dSAndroid Build Coastguard Worker filterResult.timezoneInfo, 208*90c8c64dSAndroid Build Coastguard Worker ); 209*90c8c64dSAndroid Build Coastguard Worker } 210*90c8c64dSAndroid Build Coastguard Worker 211*90c8c64dSAndroid Build Coastguard Worker if (!filterResult.perfetto && filterResult.legacy.length === 0) { 212*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new NoValidFiles()); 213*90c8c64dSAndroid Build Coastguard Worker return; 214*90c8c64dSAndroid Build Coastguard Worker } 215*90c8c64dSAndroid Build Coastguard Worker 216*90c8c64dSAndroid Build Coastguard Worker const legacyParsers = await new LegacyParserFactory().createParsers( 217*90c8c64dSAndroid Build Coastguard Worker filterResult.legacy, 218*90c8c64dSAndroid Build Coastguard Worker this.timestampConverter, 219*90c8c64dSAndroid Build Coastguard Worker filterResult.metadata, 220*90c8c64dSAndroid Build Coastguard Worker progressListener, 221*90c8c64dSAndroid Build Coastguard Worker ); 222*90c8c64dSAndroid Build Coastguard Worker 223*90c8c64dSAndroid Build Coastguard Worker let perfettoParsers: FileAndParsers | undefined; 224*90c8c64dSAndroid Build Coastguard Worker 225*90c8c64dSAndroid Build Coastguard Worker if (filterResult.perfetto) { 226*90c8c64dSAndroid Build Coastguard Worker const parsers = await new PerfettoParserFactory().createParsers( 227*90c8c64dSAndroid Build Coastguard Worker filterResult.perfetto, 228*90c8c64dSAndroid Build Coastguard Worker this.timestampConverter, 229*90c8c64dSAndroid Build Coastguard Worker progressListener, 230*90c8c64dSAndroid Build Coastguard Worker ); 231*90c8c64dSAndroid Build Coastguard Worker perfettoParsers = new FileAndParsers(filterResult.perfetto, parsers); 232*90c8c64dSAndroid Build Coastguard Worker } 233*90c8c64dSAndroid Build Coastguard Worker 234*90c8c64dSAndroid Build Coastguard Worker const monotonicTimeOffset = 235*90c8c64dSAndroid Build Coastguard Worker this.loadedParsers.getLatestRealToMonotonicOffset( 236*90c8c64dSAndroid Build Coastguard Worker legacyParsers 237*90c8c64dSAndroid Build Coastguard Worker .map((fileAndParser) => fileAndParser.parser) 238*90c8c64dSAndroid Build Coastguard Worker .concat(perfettoParsers?.parsers ?? []), 239*90c8c64dSAndroid Build Coastguard Worker ); 240*90c8c64dSAndroid Build Coastguard Worker 241*90c8c64dSAndroid Build Coastguard Worker const realToBootTimeOffset = 242*90c8c64dSAndroid Build Coastguard Worker this.loadedParsers.getLatestRealToBootTimeOffset( 243*90c8c64dSAndroid Build Coastguard Worker legacyParsers 244*90c8c64dSAndroid Build Coastguard Worker .map((fileAndParser) => fileAndParser.parser) 245*90c8c64dSAndroid Build Coastguard Worker .concat(perfettoParsers?.parsers ?? []), 246*90c8c64dSAndroid Build Coastguard Worker ); 247*90c8c64dSAndroid Build Coastguard Worker 248*90c8c64dSAndroid Build Coastguard Worker if (monotonicTimeOffset !== undefined) { 249*90c8c64dSAndroid Build Coastguard Worker this.timestampConverter.setRealToMonotonicTimeOffsetNs( 250*90c8c64dSAndroid Build Coastguard Worker monotonicTimeOffset, 251*90c8c64dSAndroid Build Coastguard Worker ); 252*90c8c64dSAndroid Build Coastguard Worker } 253*90c8c64dSAndroid Build Coastguard Worker if (realToBootTimeOffset !== undefined) { 254*90c8c64dSAndroid Build Coastguard Worker this.timestampConverter.setRealToBootTimeOffsetNs(realToBootTimeOffset); 255*90c8c64dSAndroid Build Coastguard Worker } 256*90c8c64dSAndroid Build Coastguard Worker 257*90c8c64dSAndroid Build Coastguard Worker perfettoParsers?.parsers.forEach((p) => p.createTimestamps()); 258*90c8c64dSAndroid Build Coastguard Worker legacyParsers.forEach((fileAndParser) => 259*90c8c64dSAndroid Build Coastguard Worker fileAndParser.parser.createTimestamps(), 260*90c8c64dSAndroid Build Coastguard Worker ); 261*90c8c64dSAndroid Build Coastguard Worker 262*90c8c64dSAndroid Build Coastguard Worker this.loadedParsers.addParsers(legacyParsers, perfettoParsers); 263*90c8c64dSAndroid Build Coastguard Worker } 264*90c8c64dSAndroid Build Coastguard Worker 265*90c8c64dSAndroid Build Coastguard Worker private makeDownloadArchiveFilename( 266*90c8c64dSAndroid Build Coastguard Worker files: File[], 267*90c8c64dSAndroid Build Coastguard Worker source: FilesSource, 268*90c8c64dSAndroid Build Coastguard Worker ): string { 269*90c8c64dSAndroid Build Coastguard Worker // set download archive file name, used to download all traces 270*90c8c64dSAndroid Build Coastguard Worker let filenameWithCurrTime: string; 271*90c8c64dSAndroid Build Coastguard Worker const currTime = new Date().toISOString().slice(0, -5).replace('T', '_'); 272*90c8c64dSAndroid Build Coastguard Worker if (!this.downloadArchiveFilename && files.length === 1) { 273*90c8c64dSAndroid Build Coastguard Worker const filenameNoDir = FileUtils.removeDirFromFileName(files[0].name); 274*90c8c64dSAndroid Build Coastguard Worker const filenameNoDirOrExt = 275*90c8c64dSAndroid Build Coastguard Worker FileUtils.removeExtensionFromFilename(filenameNoDir); 276*90c8c64dSAndroid Build Coastguard Worker filenameWithCurrTime = `${filenameNoDirOrExt}_${currTime}`; 277*90c8c64dSAndroid Build Coastguard Worker } else { 278*90c8c64dSAndroid Build Coastguard Worker filenameWithCurrTime = `${source}_${currTime}`; 279*90c8c64dSAndroid Build Coastguard Worker } 280*90c8c64dSAndroid Build Coastguard Worker 281*90c8c64dSAndroid Build Coastguard Worker const archiveFilenameNoIllegalChars = filenameWithCurrTime.replace( 282*90c8c64dSAndroid Build Coastguard Worker FileUtils.ILLEGAL_FILENAME_CHARACTERS_REGEX, 283*90c8c64dSAndroid Build Coastguard Worker '_', 284*90c8c64dSAndroid Build Coastguard Worker ); 285*90c8c64dSAndroid Build Coastguard Worker if (FileUtils.DOWNLOAD_FILENAME_REGEX.test(archiveFilenameNoIllegalChars)) { 286*90c8c64dSAndroid Build Coastguard Worker return archiveFilenameNoIllegalChars; 287*90c8c64dSAndroid Build Coastguard Worker } else { 288*90c8c64dSAndroid Build Coastguard Worker console.error( 289*90c8c64dSAndroid Build Coastguard Worker "Cannot convert uploaded archive filename to acceptable format for download. Defaulting download filename to 'winscope.zip'.", 290*90c8c64dSAndroid Build Coastguard Worker ); 291*90c8c64dSAndroid Build Coastguard Worker return 'winscope'; 292*90c8c64dSAndroid Build Coastguard Worker } 293*90c8c64dSAndroid Build Coastguard Worker } 294*90c8c64dSAndroid Build Coastguard Worker 295*90c8c64dSAndroid Build Coastguard Worker private async unzipFiles( 296*90c8c64dSAndroid Build Coastguard Worker files: File[], 297*90c8c64dSAndroid Build Coastguard Worker progressListener: ProgressListener | undefined, 298*90c8c64dSAndroid Build Coastguard Worker ): Promise<UnzippedArchive[]> { 299*90c8c64dSAndroid Build Coastguard Worker const unzippedArchives: UnzippedArchive[] = []; 300*90c8c64dSAndroid Build Coastguard Worker const progressMessage = 'Unzipping files...'; 301*90c8c64dSAndroid Build Coastguard Worker 302*90c8c64dSAndroid Build Coastguard Worker progressListener?.onProgressUpdate(progressMessage, 0); 303*90c8c64dSAndroid Build Coastguard Worker 304*90c8c64dSAndroid Build Coastguard Worker const currArchive: UnzippedArchive = []; 305*90c8c64dSAndroid Build Coastguard Worker for (let i = 0; i < files.length; i++) { 306*90c8c64dSAndroid Build Coastguard Worker let file = files[i]; 307*90c8c64dSAndroid Build Coastguard Worker 308*90c8c64dSAndroid Build Coastguard Worker const onSubProgressUpdate = (subPercentage: number) => { 309*90c8c64dSAndroid Build Coastguard Worker const totalPercentage = 310*90c8c64dSAndroid Build Coastguard Worker (100 * i) / files.length + subPercentage / files.length; 311*90c8c64dSAndroid Build Coastguard Worker progressListener?.onProgressUpdate(progressMessage, totalPercentage); 312*90c8c64dSAndroid Build Coastguard Worker }; 313*90c8c64dSAndroid Build Coastguard Worker 314*90c8c64dSAndroid Build Coastguard Worker if (await FileUtils.isGZipFile(file)) { 315*90c8c64dSAndroid Build Coastguard Worker file = await FileUtils.decompressGZipFile(file); 316*90c8c64dSAndroid Build Coastguard Worker } 317*90c8c64dSAndroid Build Coastguard Worker 318*90c8c64dSAndroid Build Coastguard Worker if (await FileUtils.isZipFile(file)) { 319*90c8c64dSAndroid Build Coastguard Worker try { 320*90c8c64dSAndroid Build Coastguard Worker const subFiles = await FileUtils.unzipFile(file, onSubProgressUpdate); 321*90c8c64dSAndroid Build Coastguard Worker const subTraceFiles = subFiles.map((subFile) => { 322*90c8c64dSAndroid Build Coastguard Worker return new TraceFile(subFile, file); 323*90c8c64dSAndroid Build Coastguard Worker }); 324*90c8c64dSAndroid Build Coastguard Worker unzippedArchives.push([...subTraceFiles]); 325*90c8c64dSAndroid Build Coastguard Worker onSubProgressUpdate(100); 326*90c8c64dSAndroid Build Coastguard Worker } catch (e) { 327*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new CorruptedArchive(file)); 328*90c8c64dSAndroid Build Coastguard Worker } 329*90c8c64dSAndroid Build Coastguard Worker } else { 330*90c8c64dSAndroid Build Coastguard Worker currArchive.push(new TraceFile(file, undefined)); 331*90c8c64dSAndroid Build Coastguard Worker } 332*90c8c64dSAndroid Build Coastguard Worker } 333*90c8c64dSAndroid Build Coastguard Worker if (currArchive.length > 0) { 334*90c8c64dSAndroid Build Coastguard Worker unzippedArchives.push(currArchive); 335*90c8c64dSAndroid Build Coastguard Worker } 336*90c8c64dSAndroid Build Coastguard Worker progressListener?.onProgressUpdate(progressMessage, 100); 337*90c8c64dSAndroid Build Coastguard Worker 338*90c8c64dSAndroid Build Coastguard Worker return unzippedArchives; 339*90c8c64dSAndroid Build Coastguard Worker } 340*90c8c64dSAndroid Build Coastguard Worker 341*90c8c64dSAndroid Build Coastguard Worker private removeTracesAndParsersByType(type: TraceType) { 342*90c8c64dSAndroid Build Coastguard Worker const traces = this.traces.getTraces(type); 343*90c8c64dSAndroid Build Coastguard Worker traces.forEach((trace) => { 344*90c8c64dSAndroid Build Coastguard Worker this.removeTrace(trace, true); 345*90c8c64dSAndroid Build Coastguard Worker }); 346*90c8c64dSAndroid Build Coastguard Worker } 347*90c8c64dSAndroid Build Coastguard Worker} 348