Add fallback mechanism to detect correct file type

When upload an archive for files that don't have a magic number, it is hard to reliable detect their filetype so be tend to have false positive, this adds a check to try and match the filename to keywords for better autodetection

Test: Manually upload a bug report and make sure the correct files are matched
Change-Id: I12fde431498785a7f8674e495805f009a6fb9f2c
diff --git a/tools/winscope/src/DataInput.vue b/tools/winscope/src/DataInput.vue
index 4511d46..bb07106 100644
--- a/tools/winscope/src/DataInput.vue
+++ b/tools/winscope/src/DataInput.vue
@@ -272,16 +272,14 @@
       // A file is overridden if a file of the same type is upload twice, as
       // Winscope currently only support at most one file to each type
       const overriddenFileTypes = new Set();
-      const overriddenFiles = {}; // filetype => array of file names
-      let overriddenCount = 0;
+      const overriddenFiles = {}; // filetype => array of files
       for (const decodedFile of decodedFiles) {
         const dataType = decodedFile.filetype;
 
         if (decodedFileTypes.has(dataType)) {
           overriddenFileTypes.add(dataType);
           (overriddenFiles[dataType] = overriddenFiles[dataType] || [])
-              .push(this.dataFiles[dataType].filename);
-          overriddenCount++;
+              .push(this.dataFiles[dataType]);
         }
         decodedFileTypes.add(dataType);
 
@@ -289,58 +287,137 @@
             dataType, decodedFile.data);
       }
 
-      if (overriddenFileTypes.size > 0) {
-        if (overriddenFileTypes.size === 1 && overriddenCount === 1) {
-          const type = overriddenFileTypes.values().next().value;
-          const overriddenFile = overriddenFiles[type][0];
-          const keptFile = this.dataFiles[type].filename;
-          const message =
-            `'${overriddenFile}' is conflicting with '${keptFile}'. ` +
-            `Only '${keptFile}' will be kept. If you wish to display ` +
-            `'${overriddenFile}', please upload it again with no other file ` +
-            `of the same type.`;
+      // TODO(b/169305853): Remove this once we have magic numbers or another
+      // way to detect the file type more reliably.
+      for (const dataType in overriddenFiles) {
+        if (overriddenFiles.hasOwnProperty(dataType)) {
+          const files = overriddenFiles[dataType];
+          files.push(this.dataFiles[dataType]);
 
-          this.showSnackbarMessage(`WARNING: ${message}`, Infinity);
-          console.warn(message);
-        } else {
-          const message = `Mutiple conflicting files have been uploaded. ` +
-            `${overriddenCount} files have been discarded. Please check the ` +
-            `developer console for more information.`;
-          this.showSnackbarMessage(`WARNING: ${message}`, Infinity);
+          const selectedFile =
+              this.getMostLikelyCandidateFile(dataType, files);
+          this.$set(this.dataFiles, dataType, selectedFile);
 
-          const messageBuilder = [];
-          for (const type of overriddenFileTypes.values()) {
-            const keptFile = this.dataFiles[type].filename;
-            const overriddenFilesCount = overriddenFiles[type].length;
-
-            messageBuilder.push(`${overriddenFilesCount} file` +
-                `${overriddenFilesCount > 1 ? 's' : ''} of type ${type} ` +
-                `${overriddenFilesCount > 1 ? 'have' : 'has'} been ` +
-                `overridden. Only '${keptFile}' has been kept.`);
-          }
-
-          messageBuilder.push('');
-          messageBuilder.push('Please reupload the specific files you want ' +
-            'to read (one of each type).');
-          messageBuilder.push('');
-
-          messageBuilder.push('===============DISCARDED FILES===============');
-
-          for (const type of overriddenFileTypes.values()) {
-            const discardedFiles = overriddenFiles[type];
-
-            messageBuilder.push(`The following files of type ${type} ` +
-              `have been discarded:`);
-            for (const discardedFile of discardedFiles) {
-              messageBuilder.push(`  - ${discardedFile}`);
-            }
-            messageBuilder.push('');
-          }
-
-          console.warn(messageBuilder.join('\n'));
+          // Remove selected file from overriden list
+          const index = files.indexOf(selectedFile);
+          files.splice(index, 1);
         }
       }
+
+      if (overriddenFileTypes.size > 0) {
+        this.displayFilesOverridenWarning(overriddenFiles);
+      }
     },
+
+    /**
+     * Gets the file that is most likely to be the actual file of that type out
+     * of all the candidateFiles. This is required because there are some file
+     * types that have no magic number and may lead to false positives when
+     * decoding in decode.js. (b/169305853)
+     * @param {string} dataType - The type of the candidate files.
+     * @param {files[]} candidateFiles - The list all the files detected to be
+     *                                   of type dataType, passed in the order
+     *                                   they are detected/uploaded in.
+     * @return {file} - the most likely candidate.
+     */
+    getMostLikelyCandidateFile(dataType, candidateFiles) {
+      const keyWordsByDataType = {
+        [FILE_TYPES.WINDOW_MANAGER_DUMP]: 'window',
+        [FILE_TYPES.SURFACE_FLINGER_DUMP]: 'surface',
+      };
+
+      if (
+        !candidateFiles ||
+        !candidateFiles.length ||
+        candidateFiles.length == 0
+      ) {
+        throw new Error('No candidate files provided');
+      }
+
+      if (!keyWordsByDataType.hasOwnProperty(dataType)) {
+        console.warn(`setMostLikelyCandidateFile doesn't know how to handle ` +
+            `candidates of dataType ${dataType} – setting last candidate as ` +
+            `target file.`);
+
+        // We want to return the last candidate file so that, we always override
+        // old uploaded files with once of the latest uploaded files.
+        return candidateFiles.slice(-1)[0];
+      }
+
+      for (const file of candidateFiles) {
+        if (file.filename
+            .toLowerCase().includes(keyWordsByDataType[dataType])) {
+          return file;
+        }
+      }
+
+      // We want to return the last candidate file so that, we always override
+      // old uploaded files with once of the latest uploaded files.
+      return candidateFiles.slice(-1)[0];
+    },
+
+    /**
+     * Display a snackbar warning that files have been overriden and any
+     * relavant additional information in the logs.
+     * @param {{string: file[]}} overriddenFiles - a mapping from data types to
+     * the files of the of that datatype tha have been overriden.
+     */
+    displayFilesOverridenWarning(overriddenFiles) {
+      const overriddenFileTypes = Object.keys(overriddenFiles);
+      const overriddenCount = Object.values(overriddenFiles)
+          .map((files) => files.length).reduce((length, next) => length + next);
+
+      if (overriddenFileTypes.length === 1 && overriddenCount === 1) {
+        const type = overriddenFileTypes.values().next().value;
+        const overriddenFile = overriddenFiles[type][0].filename;
+        const keptFile = this.dataFiles[type].filename;
+        const message =
+          `'${overriddenFile}' is conflicting with '${keptFile}'. ` +
+          `Only '${keptFile}' will be kept. If you wish to display ` +
+          `'${overriddenFile}', please upload it again with no other file ` +
+          `of the same type.`;
+
+        this.showSnackbarMessage(`WARNING: ${message}`, Infinity);
+        console.warn(message);
+      } else {
+        const message = `Mutiple conflicting files have been uploaded. ` +
+          `${overriddenCount} files have been discarded. Please check the ` +
+          `developer console for more information.`;
+        this.showSnackbarMessage(`WARNING: ${message}`, Infinity);
+
+        const messageBuilder = [];
+        for (const type of overriddenFileTypes.values()) {
+          const keptFile = this.dataFiles[type].filename;
+          const overriddenFilesCount = overriddenFiles[type].length;
+
+          messageBuilder.push(`${overriddenFilesCount} file` +
+              `${overriddenFilesCount > 1 ? 's' : ''} of type ${type} ` +
+              `${overriddenFilesCount > 1 ? 'have' : 'has'} been ` +
+              `overridden. Only '${keptFile}' has been kept.`);
+        }
+
+        messageBuilder.push('');
+        messageBuilder.push('Please reupload the specific files you want ' +
+          'to read (one of each type).');
+        messageBuilder.push('');
+
+        messageBuilder.push('===============DISCARDED FILES===============');
+
+        for (const type of overriddenFileTypes.values()) {
+          const discardedFiles = overriddenFiles[type];
+
+          messageBuilder.push(`The following files of type ${type} ` +
+            `have been discarded:`);
+          for (const discardedFile of discardedFiles) {
+            messageBuilder.push(`  - ${discardedFile.filename}`);
+          }
+          messageBuilder.push('');
+        }
+
+        console.warn(messageBuilder.join('\n'));
+      }
+    },
+
     getFileExtensions(file) {
       const split = file.name.split('.');
       if (split.length > 1) {
diff --git a/tools/winscope/src/decode.js b/tools/winscope/src/decode.js
index 17c8a0e..eb72e08 100644
--- a/tools/winscope/src/decode.js
+++ b/tools/winscope/src/decode.js
@@ -454,6 +454,7 @@
     return decodedFile(FILE_TYPES.LAUNCHER, buffer, fileName, store);
   }
 
+  // TODO(b/169305853): Add magic number at beginning of file for better auto detection
   for (const [filetype, condition] of [
     [FILE_TYPES.TRANSACTIONS_TRACE, (file) => file.data.length > 0],
     [FILE_TYPES.WAYLAND_DUMP, (file) => (file.data.length > 0 && file.data.children[0] > 0) || file.data.length > 1],