Fixes crash (race cond) when using toast that is immediately cancelled

Running cancel after toast is shown and adding some UI stress (or sleep
on UI thread) causes a crash from toast when trying to add the toast
window to the display. The toast must be triggered from app that is
above N MR1 (25).

The steps that crash the app are:
1. Show toast (Toast.makeText(...).show()), window token is created
2. Immediately cancel toast (Toast.cancel()), window token is removed
3. Stall UI thread (Thread.sleep, heavy task), both show and cancel
events are queued to UI thread from window manager
4. Crash trying to add toast but no window token exists

In Toast:handleShow(), the mNextView is required to add the toast to
display, if the mNextView is null before posting to window manager, then
when handleShow() runs later, it will ignore adding the toast to
display. The issue before is that mNextView is set to null after cancel
runs back from window manager in UI thread but the show post will always
happen first. Therefore set mNextView to null at the beginning of
cancel will ignore adding the toast to display and avoid the crash.

Bug: 37606432

Test: manual - write an app to Toast.show(), Toast.cancel(), then
Thread.sleep(), set app's sdk usage above 25 (N MR1) and show the
toast

Change-Id: I352e296c47b1b8776c78b6b0943b1dc809963026
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index bf0601d..a919360 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -416,6 +416,11 @@
         public void handleShow(IBinder windowToken) {
             if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
                     + " mNextView=" + mNextView);
+            // If a cancel/hide is pending - no need to show - at this point
+            // the window token is already invalid and no need to do any work.
+            if (mHandler.hasMessages(CANCEL) || mHandler.hasMessages(HIDE)) {
+                return;
+            }
             if (mView != mNextView) {
                 // remove the old view if necessary
                 handleHide();
@@ -450,8 +455,16 @@
                     mWM.removeView(mView);
                 }
                 if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
-                mWM.addView(mView, mParams);
-                trySendAccessibilityEvent();
+                // Since the notification manager service cancels the token right
+                // after it notifies us to cancel the toast there is an inherent
+                // race and we may attempt to add a window after the token has been
+                // invalidated. Let us hedge against that.
+                try {
+                    mWM.addView(mView, mParams);
+                    trySendAccessibilityEvent();
+                } catch (WindowManager.BadTokenException e) {
+                    /* ignore */
+                }
             }
         }