Fix status bar app name/icon overlap

This issue was caused by a missing check for if there is a
waiting entry for the given key in BaseHeadsUpManager.removeEntry

Also add more stack call tracing to HeadsUpManagerLogger to
improve debugging

Fixes: 340334234
Test: manual with test app:
      send default notifs to get notif icons in status bar
      tap add 3 times fast
      during the initial appear animation, tap cancel all
      => see that app name is replaced by app icons
Change-Id: I5d7a59e83cf98d96297d40750ed7d8243b098709
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 4c3c7d5..8841870 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -159,7 +159,7 @@
             public void onLayoutChange(View v, int left, int top, int right, int bottom,
                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
                 if (shouldBeVisible()) {
-                    updateTopEntry();
+                    updateTopEntry("onLayoutChange");
 
                     // trigger scroller to notify the latest panel translation
                     mStackScrollerController.requestLayout();
@@ -220,7 +220,7 @@
 
     @Override
     public void onHeadsUpPinned(NotificationEntry entry) {
-        updateTopEntry();
+        updateTopEntry("onHeadsUpPinned");
         updateHeader(entry);
         updateHeadsUpAndPulsingRoundness(entry);
     }
@@ -231,7 +231,7 @@
         mPhoneStatusBarTransitions.onHeadsUpStateChanged(isHeadsUp);
     }
 
-    private void updateTopEntry() {
+    private void updateTopEntry(String reason) {
         NotificationEntry newEntry = null;
         if (shouldBeVisible()) {
             newEntry = mHeadsUpManager.getTopEntry();
@@ -369,7 +369,7 @@
 
     @Override
     public void onHeadsUpUnPinned(NotificationEntry entry) {
-        updateTopEntry();
+        updateTopEntry("onHeadsUpUnPinned");
         updateHeader(entry);
         updateHeadsUpAndPulsingRoundness(entry);
     }
@@ -387,7 +387,7 @@
             updateHeadsUpHeaders();
         }
         if (isExpanded() != oldIsExpanded) {
-            updateTopEntry();
+            updateTopEntry("setAppearFraction");
         }
     }
 
@@ -461,11 +461,11 @@
     }
 
     public void onStateChanged() {
-        updateTopEntry();
+        updateTopEntry("onStateChanged");
     }
 
     @Override
     public void onFullyHiddenChanged(boolean isFullyHidden) {
-        updateTopEntry();
+        updateTopEntry("onFullyHiddenChanged");
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 68457ea..ffc859e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -249,7 +249,7 @@
             for (NotificationEntry entry : mEntriesToRemoveAfterExpand) {
                 if (isHeadsUpEntry(entry.getKey())) {
                     // Maybe the heads-up was removed already
-                    removeEntry(entry.getKey());
+                    removeEntry(entry.getKey(), "onExpandingFinished");
                 }
             }
         }
@@ -381,7 +381,7 @@
         for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
             if (isHeadsUpEntry(entry.getKey())) {
                 // Maybe the heads-up was removed already
-                removeEntry(entry.getKey());
+                removeEntry(entry.getKey(), "mOnReorderingAllowedListener");
             }
         }
         mEntriesToRemoveWhenReorderingAllowed.clear();
@@ -572,7 +572,7 @@
                 } else if (mTrackingHeadsUp) {
                     mEntriesToRemoveAfterExpand.add(entry);
                 } else {
-                    removeEntry(entry.getKey());
+                    removeEntry(entry.getKey(), "createRemoveRunnable");
                 }
             };
         }
@@ -661,7 +661,7 @@
                     }
                 }
                 for (String key : keysToRemove) {
-                    removeEntry(key);
+                    removeEntry(key, "mStatusBarStateListener");
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 8b48bd3..2169154 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -79,7 +79,8 @@
             runnable.run()
             return
         }
-        val fn = "[$label] => AvalancheController.update [${getKey(entry)}]"
+        log { "\n "}
+        val fn = "$label => AvalancheController.update ${getKey(entry)}"
         if (entry == null) {
             log { "Entry is NULL, stop update." }
             return
@@ -88,13 +89,13 @@
             debugRunnableLabelMap[runnable] = label
         }
         if (isShowing(entry)) {
-            log { "\n$fn => [update showing]" }
+            log { "\n$fn => update showing" }
             runnable.run()
         } else if (entry in nextMap) {
-            log { "\n$fn => [update next]" }
+            log { "\n$fn => update next" }
             nextMap[entry]?.add(runnable)
         } else if (headsUpEntryShowing == null) {
-            log { "\n$fn => [showNow]" }
+            log { "\n$fn => showNow" }
             showNow(entry, arrayListOf(runnable))
         } else {
             // Clean up invalid state when entry is in list but not map and vice versa
@@ -133,20 +134,22 @@
             runnable.run()
             return
         }
-        val fn = "[$label] => AvalancheController.delete " + getKey(entry)
+        log { "\n "}
+        val fn = "$label => AvalancheController.delete " + getKey(entry)
         if (entry == null) {
-            log { "$fn => cannot remove NULL entry" }
+            log { "$fn => entry NULL, running runnable" }
+            runnable.run()
             return
         }
         if (entry in nextMap) {
-            log { "$fn => [remove from next]" }
+            log { "$fn => remove from next" }
             if (entry in nextMap) nextMap.remove(entry)
             if (entry in nextList) nextList.remove(entry)
         } else if (entry in debugDropSet) {
-            log { "$fn => [remove from dropset]" }
+            log { "$fn => remove from dropset" }
             debugDropSet.remove(entry)
         } else if (isShowing(entry)) {
-            log { "$fn => [remove showing ${getKey(entry)}]" }
+            log { "$fn => remove showing ${getKey(entry)}" }
             previousHunKey = getKey(headsUpEntryShowing)
             // Show the next HUN before removing this one, so that we don't tell listeners
             // onHeadsUpPinnedModeChanged, which causes
@@ -155,7 +158,7 @@
             showNext()
             runnable.run()
         } else {
-            log { "$fn => [removing untracked ${getKey(entry)}]" }
+            log { "$fn => removing untracked ${getKey(entry)}" }
         }
         logState("after $fn")
     }
@@ -239,6 +242,18 @@
         return keyList
     }
 
+    fun getWaitingEntry(key: String): HeadsUpEntry? {
+        if (!NotificationThrottleHun.isEnabled) {
+            return null
+        }
+        for (headsUpEntry in nextMap.keys) {
+            if (headsUpEntry.mEntry?.key.equals(key)) {
+                return headsUpEntry
+            }
+        }
+        return null
+    }
+
     private fun isShowing(entry: HeadsUpEntry): Boolean {
         return headsUpEntryShowing != null && entry.mEntry?.key == headsUpEntryShowing?.mEntry?.key
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index a7fe49b..2ee98bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -195,26 +195,29 @@
      */
     @Override
     public boolean removeNotification(@NonNull String key, boolean releaseImmediately) {
-        mLogger.logRemoveNotification(key, releaseImmediately);
+        final boolean isWaiting = mAvalancheController.isWaiting(key);
+        mLogger.logRemoveNotification(key, releaseImmediately, isWaiting);
 
         if (mAvalancheController.isWaiting(key)) {
-            removeEntry(key);
+            removeEntry(key, "removeNotification (isWaiting)");
             return true;
         }
         HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
         if (headsUpEntry == null) {
             return true;
         }
-        if (releaseImmediately || canRemoveImmediately(key)) {
-            removeEntry(key);
-        } else {
-            headsUpEntry.removeAsSoonAsPossible();
-            return false;
+        if (releaseImmediately) {
+            removeEntry(key, "removeNotification (releaseImmediately)");
+            return true;
         }
-        return true;
+        if (canRemoveImmediately(key)) {
+            removeEntry(key, "removeNotification (canRemoveImmediately)");
+            return true;
+        }
+        headsUpEntry.removeAsSoonAsPossible();
+        return false;
     }
 
-
     /**
      * Called when the notification state has been updated.
      * @param key the key of the entry that was updated
@@ -245,7 +248,8 @@
         if (shouldHeadsUpAgain) {
             headsUpEntry.updateEntry(true /* updatePostTime */, "updateNotification");
             if (headsUpEntry != null) {
-                setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry));
+                setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry),
+                        "updateNotificationInternal");
             }
         }
     }
@@ -264,10 +268,10 @@
         List<String> waitingKeysToRemove = mAvalancheController.getWaitingKeys();
 
         for (String key : keysToRemove) {
-            removeEntry(key);
+            removeEntry(key, "releaseAllImmediately (keysToRemove)");
         }
         for (String key : waitingKeysToRemove) {
-            removeEntry(key);
+            removeEntry(key, "releaseAllImmediately (waitingKeysToRemove)");
         }
     }
 
@@ -338,8 +342,9 @@
     }
 
     protected void setEntryPinned(
-            @NonNull BaseHeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
-        mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned);
+            @NonNull BaseHeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned,
+            String reason) {
+        mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned, reason);
         NotificationEntry entry = headsUpEntry.mEntry;
         if (!isPinned) {
             headsUpEntry.mWasUnpinned = true;
@@ -376,7 +381,7 @@
         entry.setHeadsUp(true);
 
         final boolean shouldPin = shouldHeadsUpBecomePinned(entry);
-        setEntryPinned(headsUpEntry, shouldPin);
+        setEntryPinned(headsUpEntry, shouldPin, "onEntryAdded");
         EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 1 /* visible */);
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, true);
@@ -387,17 +392,24 @@
      * Remove a notification from the alerting entries.
      * @param key key of notification to remove
      */
-    protected final void removeEntry(@NonNull String key) {
+    protected final void removeEntry(@NonNull String key, String reason) {
         HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
-        mLogger.logRemoveEntryRequest(key);
-
+        boolean isWaiting;
+        if (headsUpEntry == null) {
+            headsUpEntry = mAvalancheController.getWaitingEntry(key);
+            isWaiting = true;
+        } else {
+            isWaiting = false;
+        }
+        mLogger.logRemoveEntryRequest(key, reason, isWaiting);
+        HeadsUpEntry finalHeadsUpEntry = headsUpEntry;
         Runnable runnable = () -> {
-            mLogger.logRemoveEntry(key);
+            mLogger.logRemoveEntry(key, reason, isWaiting);
 
-            if (headsUpEntry == null) {
+            if (finalHeadsUpEntry == null) {
                 return;
             }
-            NotificationEntry entry = headsUpEntry.mEntry;
+            NotificationEntry entry = finalHeadsUpEntry.mEntry;
 
             // If the notification is animating, we will remove it at the end of the animation.
             if (entry != null && entry.isExpandAnimationRunning()) {
@@ -405,13 +417,13 @@
             }
             entry.demoteStickyHun();
             mHeadsUpEntryMap.remove(key);
-            onEntryRemoved(headsUpEntry);
+            onEntryRemoved(finalHeadsUpEntry);
             // TODO(b/328390331) move accessibility events to the view layer
             entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
             if (NotificationsHeadsUpRefactor.isEnabled()) {
-                headsUpEntry.cancelAutoRemovalCallbacks("removeEntry");
+                finalHeadsUpEntry.cancelAutoRemovalCallbacks("removeEntry");
             } else {
-                headsUpEntry.reset();
+                finalHeadsUpEntry.reset();
             }
         };
         mAvalancheController.delete(headsUpEntry, runnable, "removeEntry");
@@ -424,7 +436,7 @@
     protected void onEntryRemoved(HeadsUpEntry headsUpEntry) {
         NotificationEntry entry = headsUpEntry.mEntry;
         entry.setHeadsUp(false);
-        setEntryPinned(headsUpEntry, false /* isPinned */);
+        setEntryPinned(headsUpEntry, false /* isPinned */, "onEntryRemoved");
         EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 0 /* visible */);
         mLogger.logNotificationActuallyRemoved(entry);
         for (OnHeadsUpChangedListener listener : mListeners) {
@@ -584,7 +596,7 @@
             Runnable runnable = () -> {
                 mLogger.logUnpinEntry(key);
 
-                setEntryPinned(headsUpEntry, false /* isPinned */);
+                setEntryPinned(headsUpEntry, false /* isPinned */, "unpinAll");
                 // maybe it got un sticky
                 headsUpEntry.updateEntry(false /* updatePostTime */, "unpinAll");
 
@@ -985,7 +997,7 @@
 
         /** Creates a runnable to remove this notification from the alerting entries. */
         protected Runnable createRemoveRunnable(NotificationEntry entry) {
-            return () -> removeEntry(entry.getKey());
+            return () -> removeEntry(entry.getKey(), "createRemoveRunnable");
         }
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index 11cbc9c..6ffb162 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -121,19 +121,23 @@
         })
     }
 
-    fun logRemoveEntryRequest(key: String) {
+    fun logRemoveEntryRequest(key: String, reason: String, isWaiting: Boolean) {
         buffer.log(TAG, INFO, {
             str1 = logKey(key)
+            str2 = reason
+            bool1 = isWaiting
         }, {
-            "request: remove entry $str1"
+            "request: $str2 => remove entry $str1 isWaiting: $isWaiting"
         })
     }
 
-    fun logRemoveEntry(key: String) {
+    fun logRemoveEntry(key: String, reason: String, isWaiting: Boolean) {
         buffer.log(TAG, INFO, {
             str1 = logKey(key)
+            str2 = reason
+            bool1 = isWaiting
         }, {
-            "remove entry $str1"
+            "$str2 => remove entry $str1 isWaiting: $isWaiting"
         })
     }
 
@@ -153,12 +157,13 @@
         })
     }
 
-    fun logRemoveNotification(key: String, releaseImmediately: Boolean) {
+    fun logRemoveNotification(key: String, releaseImmediately: Boolean, isWaiting: Boolean) {
         buffer.log(TAG, INFO, {
             str1 = logKey(key)
             bool1 = releaseImmediately
+            bool2 = isWaiting
         }, {
-            "remove notification $str1 releaseImmediately: $bool1"
+            "remove notification $str1 releaseImmediately: $bool1 isWaiting: $bool2"
         })
     }
 
@@ -208,12 +213,13 @@
         })
     }
 
-    fun logSetEntryPinned(entry: NotificationEntry, isPinned: Boolean) {
+    fun logSetEntryPinned(entry: NotificationEntry, isPinned: Boolean, reason: String) {
         buffer.log(TAG, VERBOSE, {
             str1 = entry.logKey
             bool1 = isPinned
+            str2 = reason
         }, {
-            "set entry pinned $str1 pinned: $bool1"
+            "$str2 => set entry pinned $str1 pinned: $bool1"
         })
     }