From eac0ea5cdfade6bfd2a1e40e67c2a45d4c37ab9e Mon Sep 17 00:00:00 2001 From: Andrii Kulian Date: Wed, 11 May 2016 15:50:24 -0700 Subject: [PATCH] Close leaked windows when trying to preserve main one When app has several windows and activity is relaunched + we try to preserve main window - other windows just stayed around until removed by timeout or replaced by app. There was a problem when one of the windows registered broadcast receiver and set its own timer to remove it. In this case all receivers were removed by framework because windows were considered leaked and apps' timer caused crash when trying to remove registered receiver. This CL removes all windows expect the main one, which we're trying to preserve in this case. Bug: 28337135 Change-Id: Ib8790cc8c61801f11d871ba3803bb0ebc3d3be01 --- core/java/android/app/ActivityThread.java | 17 +++++++----- .../android/view/WindowManagerGlobal.java | 27 +++++++++++++++---- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e54edf2a574b1..8a3306eb87480 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4183,12 +4183,10 @@ public final class ActivityThread { // window is being added. r.mPendingRemoveWindow = r.window; r.mPendingRemoveWindowManager = wm; - if (r.mPreserveWindow) { - // We can only keep the part of the view hierarchy that we control, - // everything else must be removed, because it might not be able to - // behave properly when activity is relaunching. - r.window.clearContentView(); - } + // We can only keep the part of the view hierarchy that we control, + // everything else must be removed, because it might not be able to + // behave properly when activity is relaunching. + r.window.clearContentView(); } else { wm.removeViewImmediate(v); } @@ -4196,6 +4194,13 @@ public final class ActivityThread { if (wtoken != null && r.mPendingRemoveWindow == null) { WindowManagerGlobal.getInstance().closeAll(wtoken, r.activity.getClass().getName(), "Activity"); + } else if (r.mPendingRemoveWindow != null) { + // We're preserving only one window, others should be closed so app views + // will be detached before the final tear down. It should be done now because + // some components (e.g. WebView) rely on detach callbacks to perform receiver + // unregister and other cleanup. + WindowManagerGlobal.getInstance().closeAllExceptView(token, v, + r.activity.getClass().getName(), "Activity"); } r.activity.mDecor = null; } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 887cc3acb64a4..11734d31388e9 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -391,17 +391,34 @@ public final class WindowManagerGlobal { } } + /** + * Remove all roots with specified token. + * + * @param token app or window token. + * @param who name of caller, used in logs. + * @param what type of caller, used in logs. + */ public void closeAll(IBinder token, String who, String what) { + closeAllExceptView(token, null /* view */, who, what); + } + + /** + * Remove all roots with specified token, except maybe one view. + * + * @param token app or window token. + * @param view view that should be should be preserved along with it's root. + * Pass null if everything should be removed. + * @param who name of caller, used in logs. + * @param what type of caller, used in logs. + */ + public void closeAllExceptView(IBinder token, View view, String who, String what) { synchronized (mLock) { int count = mViews.size(); - //Log.i("foo", "Closing all windows of " + token); for (int i = 0; i < count; i++) { - //Log.i("foo", "@ " + i + " token " + mParams[i].token - // + " view " + mRoots[i].getView()); - if (token == null || mParams.get(i).token == token) { + if ((view == null || mViews.get(i) != view) + && (token == null || mParams.get(i).token == token)) { ViewRootImpl root = mRoots.get(i); - //Log.i("foo", "Force closing " + root); if (who != null) { WindowLeaked leak = new WindowLeaked( what + " " + who + " has leaked window "