[6] Fix infinite loop for IME processing

Previously, some Winscope traces and ADB captures will go into an
infinite loop after IME processing was introduced.
The issue was a loop in filtering the SF Layer node that did not
account for multi-windowing mode or cases where the expected parent
could not be found. This commit fixes this issue.

Bug: 236679852
Test: manual with previously failing traces
Change-Id: I8d3fe42802efa195926b5be0d4242a0bb0696988
diff --git a/tools/winscope/src/ime_processing.js b/tools/winscope/src/ime_processing.js
index 2670ee3..98a48d7 100644
--- a/tools/winscope/src/ime_processing.js
+++ b/tools/winscope/src/ime_processing.js
@@ -46,11 +46,13 @@
         filetype.includes('ImeTrace'))).map(([k, v]) => v);
   for (const imeTraceFile of imeTraceFiles) {
     if (filesAsDict[TRACE_TYPES.WINDOW_MANAGER]) {
+      console.log('combining WM file to', imeTraceFile.type, 'file');
       combineWmSfPropertiesIntoImeData(imeTraceFile,
           filesAsDict[TRACE_TYPES.WINDOW_MANAGER]);
     }
     if (filesAsDict[TRACE_TYPES.SURFACE_FLINGER] &&
       imeTraceFile.type !== TRACE_TYPES.IME_MANAGERSERVICE) {
+      console.log('combining SF file to', imeTraceFile.type, 'file');
       // don't need SF properties for ime manager service
       combineWmSfPropertiesIntoImeData(imeTraceFile,
           filesAsDict[TRACE_TYPES.SURFACE_FLINGER]);
@@ -61,25 +63,14 @@
 
 function combineWmSfPropertiesIntoImeData(imeTraceFile, wmOrSfTraceFile) {
   const imeTimestamps = imeTraceFile.timeline;
-  const wmOrSfData = wmOrSfTraceFile.data;
   const wmOrSfTimestamps = wmOrSfTraceFile.timeline;
+  const intersectWmOrSfIndices =
+    matchCorrespondingTimestamps(imeTimestamps, wmOrSfTimestamps);
 
-  // find the latest sf / wm timestamp that comes before current ime timestamp
-  let wmOrSfIndex = 0;
-  const intersectWmOrSfIndices = [];
-  for (let imeIndex = 0; imeIndex < imeTimestamps.length; imeIndex++) {
-    const currImeTimestamp = imeTimestamps[imeIndex];
+  const wmOrSfData = wmOrSfTraceFile.data;
 
-    let currWmOrSfTimestamp = wmOrSfTimestamps[wmOrSfIndex];
-    while (currWmOrSfTimestamp < currImeTimestamp) {
-      wmOrSfIndex++;
-      currWmOrSfTimestamp = wmOrSfTimestamps[wmOrSfIndex];
-    }
-    intersectWmOrSfIndices.push(wmOrSfIndex - 1);
-  }
-
+  console.log('number of entries:', imeTimestamps.length);
   for (let i = 0; i < imeTimestamps.length; i++) {
-    // TODO: abstract into one function
     const wmOrSfIntersectIndex = intersectWmOrSfIndices[i];
     let wmStateOrSfLayer = wmOrSfData[wmOrSfIntersectIndex];
     if (wmStateOrSfLayer) {
@@ -106,6 +97,23 @@
   }
 }
 
+function matchCorrespondingTimestamps(imeTimestamps, wmOrSfTimestamps) {
+  // find the latest sf / wm timestamp that comes before current ime timestamp
+  let wmOrSfIndex = 0;
+  const intersectWmOrSfIndices = [];
+  for (let imeIndex = 0; imeIndex < imeTimestamps.length; imeIndex++) {
+    const currImeTimestamp = imeTimestamps[imeIndex];
+
+    let currWmOrSfTimestamp = wmOrSfTimestamps[wmOrSfIndex];
+    while (currWmOrSfTimestamp < currImeTimestamp) {
+      wmOrSfIndex++;
+      currWmOrSfTimestamp = wmOrSfTimestamps[wmOrSfIndex];
+    }
+    intersectWmOrSfIndices.push(wmOrSfIndex - 1);
+  }
+  console.log('done matching corresponding timestamps');
+  return intersectWmOrSfIndices;
+}
 
 function filterWmStateForIme(wmState) {
   // create and return a custom entry that just contains relevant properties
@@ -131,9 +139,11 @@
   let resultLayer;
   if (parentTaskName === '') {
     // there is no ImeContainer; check for ime-snapshot
+    console.log('there is no ImeContainer; checking for IME-snapshot');
     const snapshotFilter = getFilter('IME-snapshot');
     resultLayer = pruneChildrenByFilter(sfLayer, snapshotFilter);
   } else {
+    console.log('found parent task of ImeContainer:', parentTaskName);
     const imeParentTaskFilter = getFilter(parentTaskName);
     // prune all children that are not part of the "parent task" of ImeContainer
     resultLayer = pruneChildrenByFilter(sfLayer, imeParentTaskFilter);
@@ -145,13 +155,14 @@
 function findParentTaskNameOfImeContainer(curr) {
   const isImeContainer = getFilter('ImeContainer');
   if (isImeContainer(curr)) {
+    console.log('found ImeContainer; searching for parent');
     let parent = curr.parent;
-    const isTask = getFilter('Task');
-    while (parent && !isTask(parent)) {
+    const isTask = getFilter('Task, ImePlaceholder');
+    while (parent.parent && !isTask(parent)) {
+      // if parent.parent is null, 'parent' is already the root node -- use it
       if (parent.parent != null) {
         parent = parent.parent;
       }
-      // else 'parent' is already the root node; use it
     }
     return parent.name;
   }
diff --git a/tools/winscope/src/utils/utils.js b/tools/winscope/src/utils/utils.js
index db6afcc..5ee25d6 100644
--- a/tools/winscope/src/utils/utils.js
+++ b/tools/winscope/src/utils/utils.js
@@ -110,6 +110,7 @@
   const positive = [];
   const negative = [];
   filterStrings.forEach((f) => {
+    f = f.trim();
     if (f.startsWith('!')) {
       const regex = new RegExp(f.substring(1), "i");
       negative.push((s) => !regex.test(s));