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

Commit 831010e5 authored by Bryce Lee's avatar Bryce Lee
Browse files

Do not restart touch listening if TouchMonitor is destroyed.

This changelist ensures that TouchMonitor does not continue listening
and handling touches past destroy. Previously, this was possible if
touch handling ended after the monitor was destroyed. In this case, the
TouchMonitor was reset incorrectly to a listening/ready state.

Logging has also been improved with this change by adding a unique
identifier to each TouchMonitor instance for better attribution.

Test: atest TouchMonitorTest#testSessionPopAfterDestroy
Flag: EXEMPT bugfix
Fixes: 367549056
Change-Id: I16d99aab763570693144f15d102ae07ac2d9cd07
parent f8f24c07
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -75,6 +75,9 @@ import javax.inject.Named;
 * touches are consumed.
 */
public class TouchMonitor {
    // An incrementing id used to identify the touch monitor instance.
    private static int sNextInstanceId = 0;

    private final Logger mLogger;
    // This executor is used to protect {@code mActiveTouchSessions} from being modified
    // concurrently. Any operation that adds or removes values should use this executor.
@@ -138,7 +141,7 @@ public class TouchMonitor {
                    completer.set(predecessor);
                }

                if (mActiveTouchSessions.isEmpty()) {
                if (mActiveTouchSessions.isEmpty() && mInitialized) {
                    if (mStopMonitoringPending) {
                        stopMonitoring(false);
                    } else {
@@ -271,7 +274,7 @@ public class TouchMonitor {

        @Override
        public void onDestroy(LifecycleOwner owner) {
            stopMonitoring(true);
            destroy();
        }
    };

@@ -279,6 +282,11 @@ public class TouchMonitor {
     * When invoked, instantiates a new {@link InputSession} to monitor touch events.
     */
    private void startMonitoring() {
        if (!mInitialized) {
            mLogger.w("attempting to startMonitoring when not initialized");
            return;
        }

        mLogger.i("startMonitoring(): monitoring started");
        stopMonitoring(true);

@@ -587,7 +595,7 @@ public class TouchMonitor {
        mDisplayHelper = displayHelper;
        mWindowManagerService = windowManagerService;
        mConfigurationInteractor = configurationInteractor;
        mLoggingName = loggingName + ":TouchMonitor";
        mLoggingName = loggingName + ":TouchMonitor[" + sNextInstanceId++ + "]";
        mLogger = new Logger(logBuffer, mLoggingName);
    }

@@ -613,7 +621,8 @@ public class TouchMonitor {
     */
    public void destroy() {
        if (!mInitialized) {
            throw new IllegalStateException("TouchMonitor not initialized");
            // In the case that we've already been destroyed, this is a no-op
            return;
        }

        stopMonitoring(true);
+41 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import android.content.res.Configuration;
@@ -643,6 +644,46 @@ public class TouchMonitorTest extends SysuiTestCase {
        environment.verifyInputSessionDispose();
    }

    @Test
    public void testSessionPopAfterDestroy() {
        final TouchHandler touchHandler = createTouchHandler();

        final Environment environment = new Environment(Stream.of(touchHandler)
                .collect(Collectors.toCollection(HashSet::new)), mKosmos);

        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
        environment.publishInputEvent(initialEvent);

        // Ensure session started
        final InputChannelCompat.InputEventListener eventListener =
                registerInputEventListener(touchHandler);

        // First event will be missed since we register after the execution loop,
        final InputEvent event = Mockito.mock(InputEvent.class);
        environment.publishInputEvent(event);
        verify(eventListener).onInputEvent(eq(event));

        final ArgumentCaptor<TouchHandler.TouchSession> touchSessionArgumentCaptor =
                ArgumentCaptor.forClass(TouchHandler.TouchSession.class);

        verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());

        environment.updateLifecycle(Lifecycle.State.DESTROYED);

        // Check to make sure the input session is now disposed.
        environment.verifyInputSessionDispose();

        clearInvocations(environment.mInputFactory);

        // Pop the session
        touchSessionArgumentCaptor.getValue().pop();

        environment.executeAll();

        // Ensure no input sessions were created due to the session reset.
        verifyNoMoreInteractions(environment.mInputFactory);
    }


    @Test
    public void testPilfering() {