From 70f0d229c02b91d7b987c4972b9b08bb9dcd04b5 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Sun, 10 Apr 2016 16:33:08 -0700 Subject: [PATCH] Add WindowManager side timeout for toasts. If the thread a toast is shown on is shut down, the attempt to post the hide message to it's handler will fail and it will never request removal. If this was only some application background thread we will also not receive a death notification. It seems best to use a timeout to ensure we don't need the clients cooperation, espescially as toasts can keep the screen on. Bug: 21693547 Change-Id: I1d6e54ded5b9e2050daedc4d263e2e21fbe69862 --- core/java/android/view/WindowManager.java | 15 +++++++++++++++ core/java/android/widget/Toast.java | 9 ++++++++- .../server/wm/WindowManagerService.java | 19 ++++++++++++++++++- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e3de8106fb1bf..60548162b7186 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1726,6 +1726,16 @@ public interface WindowManager extends ViewManager { */ public CharSequence accessibilityTitle; + /** + * Sets a timeout in milliseconds before which the window will be removed + * by the window manager. Useful for transient notifications like toasts + * so we don't have to rely on client cooperation to ensure the window + * is removed. Must be specified at window creation time. + * + * @hide + */ + public long removeTimeoutMilliseconds = -1; + public LayoutParams() { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = TYPE_APPLICATION; @@ -1847,6 +1857,7 @@ public interface WindowManager extends ViewManager { out.writeInt(needsMenuKey); out.writeInt(accessibilityIdOfAnchor); TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags); + out.writeLong(removeTimeoutMilliseconds); } public static final Parcelable.Creator CREATOR @@ -1900,6 +1911,7 @@ public interface WindowManager extends ViewManager { needsMenuKey = in.readInt(); accessibilityIdOfAnchor = in.readInt(); accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + removeTimeoutMilliseconds = in.readLong(); } @SuppressWarnings({"PointlessBitwiseExpression"}) @@ -2120,6 +2132,9 @@ public interface WindowManager extends ViewManager { changes |= ACCESSIBILITY_TITLE_CHANGED; } + // This can't change, it's only set at window creation time. + removeTimeoutMilliseconds = o.removeTimeoutMilliseconds; + return changes; } diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 207f675945e0d..77626754264f1 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -163,6 +163,7 @@ public class Toast { */ public void setDuration(@Duration int duration) { mDuration = duration; + mTN.mDuration = duration; } /** @@ -342,7 +343,7 @@ public class Toast { }; private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); - final Handler mHandler = new Handler(); + final Handler mHandler = new Handler(); int mGravity; int mX, mY; @@ -352,9 +353,13 @@ public class Toast { View mView; View mNextView; + int mDuration; WindowManager mWM; + static final long SHORT_DURATION_TIMEOUT = 5000; + static final long LONG_DURATION_TIMEOUT = 1000; + TN() { // XXX This should be changed to use a Dialog, with a Theme.Toast // defined that sets up the layout params appropriately. @@ -417,6 +422,8 @@ public class Toast { mParams.verticalMargin = mVerticalMargin; mParams.horizontalMargin = mHorizontalMargin; mParams.packageName = packageName; + mParams.removeTimeoutMilliseconds = mDuration == + Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT; if (mView.getParent() != null) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9088522dfa8e2..d48ba0531559a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2107,6 +2107,11 @@ public class WindowManagerService extends IWindowManager.Stub if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) { reportNewConfig = true; } + if (attrs.removeTimeoutMilliseconds > 0) { + mH.sendMessageDelayed( + mH.obtainMessage(H.WINDOW_REMOVE_TIMEOUT, win), + attrs.removeTimeoutMilliseconds); + } } if (reportNewConfig) { @@ -7782,8 +7787,8 @@ public class WindowManagerService extends IWindowManager.Stub public static final int NOTIFY_APP_TRANSITION_CANCELLED = 48; public static final int NOTIFY_APP_TRANSITION_FINISHED = 49; public static final int NOTIFY_STARTING_WINDOW_DRAWN = 50; - public static final int UPDATE_ANIMATION_SCALE = 51; + public static final int WINDOW_REMOVE_TIMEOUT = 52; /** * Used to denote that an integer field in a message will not be used. @@ -8396,6 +8401,18 @@ public class WindowManagerService extends IWindowManager.Stub mAmInternal.notifyStartingWindowDrawn(); } break; + case WINDOW_REMOVE_TIMEOUT: { + final WindowState window = (WindowState) msg.obj; + synchronized(mWindowMap) { + // It's counterintuitive that we check that "mWindowRemovalAllowed" + // is false. But in fact if it's true, it means a remove has already + // been requested and we better just not do anything. + if (!window.mRemoved && !window.mWindowRemovalAllowed) { + removeWindowLocked(window); + } + } + } + break; } if (DEBUG_WINDOW_TRACE) { Slog.v(TAG_WM, "handleMessage: exit");