diff options
| author | Charles Chen <charlesccchen@google.com> | 2020-03-11 10:17:19 +0800 |
|---|---|---|
| committer | Charles Chen <charlesccchen@google.com> | 2020-04-08 21:30:24 +0800 |
| commit | 4bff5be1a91e7598731412981ed512c9060ef188 (patch) | |
| tree | 16f9ca5d4a184144776613c5a1c6c1b2e67ffe90 /core/java/android/app/WindowTokenClient.java | |
| parent | 884aa6afe156ec88cfc89e9d5dac4480223c9495 (diff) | |
Fix WindowContext leak
The root causes of this issue are:
1. WindowTokenClient#attachContext makes WindowTokenClient has strong
reference to WindowContext, which leads to WindowContext cannot be
GC'd.
2. WMS#removeWindowToken needs MANAGE_APP_TOKEN permission which
normal apps don't hold.
This CL does following things:
1. Use weak reference instead on WindowTokenClient#mContext.
2. Relax WMS#removeWindowToken to check callingUid if
MANAGE_WINDOW_TOKEN permission is not held
3. Deliver config changes to the client side in
WMS#addWindowTokenWithOption
4. Some minor fixes
fixes: 150812449
Bug: 150715095
Test: atest WindowContextTest
Test: atest WindowManagerServiceTests
Test: atest WindowTokenTests
Test: atest WindowManagerPermissionTests#testMANAGE_APP_TOKENS
Test: atest
WindowManagerPermissionTests#testADD_WINDOW_TOKEN_WITH_OPTIONS
Change-Id: I9f1d73af2abb78fc9844e6d9eb25e9f0293514e7
Diffstat (limited to 'core/java/android/app/WindowTokenClient.java')
| -rw-r--r-- | core/java/android/app/WindowTokenClient.java | 39 |
1 files changed, 29 insertions, 10 deletions
diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java index ed0179bb9839..301960ec53f9 100644 --- a/core/java/android/app/WindowTokenClient.java +++ b/core/java/android/app/WindowTokenClient.java @@ -20,6 +20,9 @@ import android.content.Context; import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; +import android.view.WindowManagerGlobal; + +import java.lang.ref.WeakReference; /** * Client implementation of {@link IWindowToken}. It can receive configuration change callbacks from @@ -31,9 +34,9 @@ import android.os.IBinder; public class WindowTokenClient extends IWindowToken.Stub { /** * Attached {@link Context} for this window token to update configuration and resources. - * Initialized by {@link #attachContext(Context)}. + * Initialized by {@link #attachContext(WindowContext)}. */ - private Context mContext = null; + private WeakReference<WindowContext> mContextRef = null; private final ResourcesManager mResourcesManager = ResourcesManager.getInstance(); @@ -47,30 +50,46 @@ public class WindowTokenClient extends IWindowToken.Stub { * @param context context to be attached * @throws IllegalStateException if attached context has already existed. */ - void attachContext(@NonNull Context context) { - if (mContext != null) { + void attachContext(@NonNull WindowContext context) { + if (mContextRef != null) { throw new IllegalStateException("Context is already attached."); } - mContext = context; - ContextImpl impl = ContextImpl.getImpl(mContext); + mContextRef = new WeakReference<>(context); + final ContextImpl impl = ContextImpl.getImpl(context); impl.setResources(impl.createWindowContextResources()); } @Override public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { - final int currentDisplayId = mContext.getDisplayId(); + final Context context = mContextRef.get(); + if (context == null) { + return; + } + final int currentDisplayId = context.getDisplayId(); final boolean displayChanged = newDisplayId != currentDisplayId; - final Configuration config = new Configuration(mContext.getResources() + final Configuration config = new Configuration(context.getResources() .getConfiguration()); final boolean configChanged = config.isOtherSeqNewer(newConfig) && config.updateFrom(newConfig) != 0; if (displayChanged || configChanged) { // TODO(ag/9789103): update resource manager logic to track non-activity tokens - mResourcesManager.updateResourcesForActivity(asBinder(), config, newDisplayId, + mResourcesManager.updateResourcesForActivity(this, config, newDisplayId, displayChanged); } if (displayChanged) { - mContext.updateDisplay(newDisplayId); + context.updateDisplay(newDisplayId); + } + } + + @Override + public void onWindowTokenRemoved() { + final WindowContext context = mContextRef.get(); + if (context != null) { + context.destroy(); + mContextRef.clear(); } + // If a secondary display is detached, release all views attached to this token. + WindowManagerGlobal.getInstance().closeAll(this, mContextRef.getClass().getName(), + "WindowContext"); } } |
