IWindowId is being kept alive by binder in certain cases, which
will leak WindowState, which can leak a whole lot including
the snapshot GraphicBuffer.
- Fix a leak in WindowContainer.mTmpChain1/2
- When the window gets removed, make sure to release the reference
to the window state so even if Binder keeps IWindowId alive, we
don't keep WindowState alive
- Make sure that even if we leak TaskSnapshotSurface we don't leak
the GraphicBuffer.
Test: Boot, some basic sanity testing.
Bug:
36279079
Change-Id: I4a0a784b32bc2df47934b1bed1d62b0682beb2dd
private final Rect mStableInsets = new Rect();
private final Rect mContentInsets = new Rect();
private final Rect mFrame = new Rect();
- private final TaskSnapshot mSnapshot;
+ private TaskSnapshot mSnapshot;
private final CharSequence mTitle;
private boolean mHasDrawn;
private long mShownTime;
mHasDrawn = true;
}
reportDrawn();
+
+ // In case window manager leaks us, make sure we don't retain the snapshot.
+ mSnapshot = null;
}
private void drawSizeMatchSnapshot(GraphicBuffer buffer) {
final LinkedList<WindowContainer> thisParentChain = mTmpChain1;
final LinkedList<WindowContainer> otherParentChain = mTmpChain2;
- getParents(thisParentChain);
- other.getParents(otherParentChain);
-
- // Find the common ancestor of both containers.
- WindowContainer commonAncestor = null;
- WindowContainer thisTop = thisParentChain.peekLast();
- WindowContainer otherTop = otherParentChain.peekLast();
- while (thisTop != null && otherTop != null && thisTop == otherTop) {
- commonAncestor = thisParentChain.removeLast();
- otherParentChain.removeLast();
- thisTop = thisParentChain.peekLast();
- otherTop = otherParentChain.peekLast();
- }
-
- // Containers don't belong to the same hierarchy???
- if (commonAncestor == null) {
- throw new IllegalArgumentException("No in the same hierarchy this="
- + thisParentChain + " other=" + otherParentChain);
- }
-
- // Children are always considered greater than their parents, so if one of the containers
- // we are comparing it the parent of the other then whichever is the child is greater.
- if (commonAncestor == this) {
- return -1;
- } else if (commonAncestor == other) {
- return 1;
- }
-
- // The position of the first non-common ancestor in the common ancestor list determines
- // which is greater the which.
- final WindowList<WindowContainer> list = commonAncestor.mChildren;
- return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
- ? 1 : -1;
+ try {
+ getParents(thisParentChain);
+ other.getParents(otherParentChain);
+
+ // Find the common ancestor of both containers.
+ WindowContainer commonAncestor = null;
+ WindowContainer thisTop = thisParentChain.peekLast();
+ WindowContainer otherTop = otherParentChain.peekLast();
+ while (thisTop != null && otherTop != null && thisTop == otherTop) {
+ commonAncestor = thisParentChain.removeLast();
+ otherParentChain.removeLast();
+ thisTop = thisParentChain.peekLast();
+ otherTop = otherParentChain.peekLast();
+ }
+
+ // Containers don't belong to the same hierarchy???
+ if (commonAncestor == null) {
+ throw new IllegalArgumentException("No in the same hierarchy this="
+ + thisParentChain + " other=" + otherParentChain);
+ }
+
+ // Children are always considered greater than their parents, so if one of the containers
+ // we are comparing it the parent of the other then whichever is the child is greater.
+ if (commonAncestor == this) {
+ return -1;
+ } else if (commonAncestor == other) {
+ return 1;
+ }
+
+ // The position of the first non-common ancestor in the common ancestor list determines
+ // which is greater the which.
+ final WindowList<WindowContainer> list = commonAncestor.mChildren;
+ return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
+ ? 1 : -1;
+ } finally {
+ mTmpChain1.clear();
+ mTmpChain2.clear();
+ }
}
private void getParents(LinkedList<WindowContainer> parents) {
import com.android.server.input.InputWindowHandle;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
final int mOwnerUid;
/** The owner has {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW} */
final boolean mOwnerCanAddInternalSystemWindow;
- final IWindowId mWindowId;
+ final WindowId mWindowId;
WindowToken mToken;
// The same object as mToken if this is an app window and null for non-app windows.
AppWindowToken mAppToken;
mAppToken = mToken.asAppWindowToken();
mOwnerUid = ownerId;
mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
- mWindowId = new IWindowId.Stub() {
- @Override
- public void registerFocusObserver(IWindowFocusObserver observer) {
- WindowState.this.registerFocusObserver(observer);
- }
- @Override
- public void unregisterFocusObserver(IWindowFocusObserver observer) {
- WindowState.this.unregisterFocusObserver(observer);
- }
- @Override
- public boolean isFocused() {
- return WindowState.this.isFocused();
- }
- };
+ mWindowId = new WindowId(this);
mAttrs.copyFrom(a);
mViewVisibility = viewVisibility;
mPolicy = mService.mPolicy;
}
}
+ private static final class WindowId extends IWindowId.Stub {
+ private final WeakReference<WindowState> mOuter;
+
+ private WindowId(WindowState outer) {
+
+ // Use a weak reference for the outer class. This is important to prevent the following
+ // leak: Since we send this class to the client process, binder will keep it alive as
+ // long as the client keeps it alive. Now, if the window is removed, we need to clear
+ // out our reference so even though this class is kept alive we don't leak WindowState,
+ // which can keep a whole lot of classes alive.
+ mOuter = new WeakReference<>(outer);
+ }
+
+ @Override
+ public void registerFocusObserver(IWindowFocusObserver observer) {
+ final WindowState outer = mOuter.get();
+ if (outer != null) {
+ outer.registerFocusObserver(observer);
+ }
+ }
+ @Override
+ public void unregisterFocusObserver(IWindowFocusObserver observer) {
+ final WindowState outer = mOuter.get();
+ if (outer != null) {
+ outer.unregisterFocusObserver(observer);
+ }
+ }
+ @Override
+ public boolean isFocused() {
+ final WindowState outer = mOuter.get();
+ return outer != null && outer.isFocused();
+ }
+ }
+
boolean usesRelativeZOrdering() {
if (!isChildWindow()) {
return false;