Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ea76811 authored by Bryce Lee's avatar Bryce Lee
Browse files

Address TouchMonitor memory leaks.

This changelist addresses TouchMonitor memory leaks by:
- Clearing TouchMonitor's reference to the gesture exclusion listener to
  prevent circular references.
- Removing lifecycle observers and lifecycle flows when the TouchMonitor
  is destroyed.
- Clearing references to touch sessions when no longer used.

Fixes: 338532926
Test: DreamOverlayServiceTest#testOnEndDream
Test: TouchMonitorTest#testDestroy_cleansUpLifecycleObserver
Flag: EXEMPT bugfix
Change-Id: I6979c8cbcd8fa9baebf962927c93325eb6eb688c
parent 8cf3a71f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -398,6 +398,9 @@ class DreamOverlayServiceTest : SysuiTestCase() {
        verify(mStateController).setOverlayActive(false)
        verify(mStateController).setLowLightActive(false)
        verify(mStateController).setEntryAnimationsFinished(false)

        // Verify touch monitor destroyed
        verify(mTouchMonitor).destroy()
    }

    @Test
+1 −0
Original line number Diff line number Diff line
@@ -269,6 +269,7 @@ public class BouncerSwipeTouchHandler implements TouchHandler {
            }
            mScrimManager.removeCallback(mScrimManagerCallback);
            mCapture = null;
            mTouchSession = null;

            if (!Flags.communalBouncerDoNotModifyPluginOpen()) {
                mNotificationShadeWindowController.setForcePluginOpen(false, this);
+47 −12
Original line number Diff line number Diff line
@@ -49,11 +49,14 @@ import com.android.systemui.util.display.DisplayHelper;

import com.google.common.util.concurrent.ListenableFuture;

import kotlinx.coroutines.Job;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -78,15 +81,7 @@ public class TouchMonitor {
    private final Lifecycle mLifecycle;
    private Rect mExclusionRect = null;

    private ISystemGestureExclusionListener mGestureExclusionListener =
            new ISystemGestureExclusionListener.Stub() {
                @Override
                public void onSystemGestureExclusionChanged(int displayId,
                        Region systemGestureExclusion,
                        Region systemGestureExclusionUnrestricted) {
                    mExclusionRect = systemGestureExclusion.getBounds();
                }
            };
    private ISystemGestureExclusionListener mGestureExclusionListener;

    private Consumer<Rect> mMaxBoundsConsumer = rect -> mMaxBounds = rect;

@@ -274,6 +269,14 @@ public class TouchMonitor {
        if (bouncerAreaExclusion()) {
            mBackgroundExecutor.execute(() -> {
                try {
                    mGestureExclusionListener = new ISystemGestureExclusionListener.Stub() {
                        @Override
                        public void onSystemGestureExclusionChanged(int displayId,
                                Region systemGestureExclusion,
                                Region systemGestureExclusionUnrestricted) {
                            mExclusionRect = systemGestureExclusion.getBounds();
                        }
                    };
                    mWindowManagerService.registerSystemGestureExclusionListener(
                            mGestureExclusionListener, mDisplayId);
                } catch (RemoteException e) {
@@ -298,8 +301,11 @@ public class TouchMonitor {
        if (bouncerAreaExclusion()) {
            mBackgroundExecutor.execute(() -> {
                try {
                    if (mGestureExclusionListener != null) {
                        mWindowManagerService.unregisterSystemGestureExclusionListener(
                                mGestureExclusionListener, mDisplayId);
                        mGestureExclusionListener = null;
                    }
                } catch (RemoteException e) {
                    // Handle the exception
                    Log.e(TAG, "unregisterSystemGestureExclusionListener: failed", e);
@@ -494,6 +500,10 @@ public class TouchMonitor {

    private Rect mMaxBounds;

    private Job mBoundsFlow;

    private boolean mInitialized;


    /**
     * Designated constructor for {@link TouchMonitor}
@@ -535,10 +545,35 @@ public class TouchMonitor {
     * Initializes the monitor. should only be called once after creation.
     */
    public void init() {
        if (mInitialized) {
            throw new IllegalStateException("TouchMonitor already initialized");
        }

        mLifecycle.addObserver(mLifecycleObserver);
        if (Flags.ambientTouchMonitorListenToDisplayChanges()) {
            collectFlow(mLifecycle, mConfigurationInteractor.getMaxBounds(), mMaxBoundsConsumer);
            mBoundsFlow = collectFlow(mLifecycle, mConfigurationInteractor.getMaxBounds(),
                    mMaxBoundsConsumer);
        }

        mInitialized = true;
    }

    /**
     * Called when the TouchMonitor should be discarded and will not be used anymore.
     */
    public void destroy() {
        if (!mInitialized) {
            throw new IllegalStateException("TouchMonitor not initialized");
        }

        stopMonitoring(true);

        mLifecycle.removeObserver(mLifecycleObserver);
        if (Flags.ambientTouchMonitorListenToDisplayChanges()) {
            mBoundsFlow.cancel(new CancellationException());
        }

        mInitialized = false;
    }

    private void isolate(Set<TouchSessionImpl> sessions) {
+5 −1
Original line number Diff line number Diff line
@@ -543,7 +543,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
        mStateController.setEntryAnimationsFinished(false);

        mDreamOverlayContainerViewController = null;

        if (mTouchMonitor != null) {
            mTouchMonitor.destroy();
            mTouchMonitor = null;
        }

        mWindow = null;
        mStarted = false;
+2 −2
Original line number Diff line number Diff line
@@ -88,8 +88,8 @@ fun <T> collectFlow(
    flow: Flow<T>,
    consumer: Consumer<T>,
    state: Lifecycle.State = Lifecycle.State.CREATED,
) {
    lifecycle.coroutineScope.launch {
): Job {
    return lifecycle.coroutineScope.launch {
        lifecycle.repeatOnLifecycle(state) { flow.collect { consumer.accept(it) } }
    }
}
Loading