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

Commit b60ee9e6 authored by Asmita Poddar's avatar Asmita Poddar
Browse files

Ensure that VirtualMouse creation is complete before usage

Use Thread.join for waiting for the virtual mouse creation to complete
before calling methods using the virtual mouse.
This approach blocks the calling thread only if the virtual mouse creation thread
is still running, and returns immediately if the thread has already completed.
This will also fix test flakiness, which were failing due to
VirtualMouse not being available yet.

Bug: 356262422
Test: atest FrameworksServicesTests:MouseKeysInterceptorTest --iterations 300
Flag: TEST_ONLY
Change-Id: I5bbbcad565dcfb5c8c71b1241828aaa005d185e0
parent 7ef1ce6e
Loading
Loading
Loading
Loading
+39 −24
Original line number Original line Diff line number Diff line
@@ -78,6 +78,9 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation
    private final AccessibilityManagerService mAms;
    private final AccessibilityManagerService mAms;
    private final Handler mHandler;
    private final Handler mHandler;


    /** Thread to wait for virtual mouse creation to complete */
    private final Thread mCreateVirtualMouseThread;

    VirtualDeviceManager.VirtualDevice mVirtualDevice = null;
    VirtualDeviceManager.VirtualDevice mVirtualDevice = null;


    private VirtualMouse mVirtualMouse = null;
    private VirtualMouse mVirtualMouse = null;
@@ -154,35 +157,48 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation
        mHandler = new Handler(looper, this);
        mHandler = new Handler(looper, this);
        // Create the virtual mouse on a separate thread since virtual device creation
        // Create the virtual mouse on a separate thread since virtual device creation
        // should happen on an auxiliary thread, and not from the handler's thread.
        // should happen on an auxiliary thread, and not from the handler's thread.
        // This is because virtual device creation is a blocking operation and can cause a
        // This is because the handler thread is the same as the main thread,
        // deadlock if it is called from the handler's thread.
        // and the main thread will be blocked waiting for the virtual device to be created.
        new Thread(() -> {
        mCreateVirtualMouseThread = new Thread(() -> {
            mVirtualMouse = createVirtualMouse(displayId);
            mVirtualMouse = createVirtualMouse(displayId);
        }).start();
        });
        mCreateVirtualMouseThread.start();
    }


    /**
     * Wait for {@code mVirtualMouse} to be created.
     * This will ensure that {@code mVirtualMouse} is always created before
     * trying to send mouse events.
     **/
    private void waitForVirtualMouseCreation() {
        try {
            // Block the current thread until the virtual mouse creation thread completes.
            mCreateVirtualMouseThread.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }
    }


    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
    private void sendVirtualMouseRelativeEvent(float x, float y) {
    private void sendVirtualMouseRelativeEvent(float x, float y) {
        if (mVirtualMouse != null) {
        waitForVirtualMouseCreation();
        mVirtualMouse.sendRelativeEvent(new VirtualMouseRelativeEvent.Builder()
        mVirtualMouse.sendRelativeEvent(new VirtualMouseRelativeEvent.Builder()
                .setRelativeX(x)
                .setRelativeX(x)
                .setRelativeY(y)
                .setRelativeY(y)
                .build()
                .build()
        );
        );
    }
    }
    }


    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
    private void sendVirtualMouseButtonEvent(int buttonCode, int actionCode) {
    private void sendVirtualMouseButtonEvent(int buttonCode, int actionCode) {
        if (mVirtualMouse != null) {
        waitForVirtualMouseCreation();
        mVirtualMouse.sendButtonEvent(new VirtualMouseButtonEvent.Builder()
        mVirtualMouse.sendButtonEvent(new VirtualMouseButtonEvent.Builder()
                .setAction(actionCode)
                .setAction(actionCode)
                .setButtonCode(buttonCode)
                .setButtonCode(buttonCode)
                .build()
                .build()
        );
        );
    }
    }
    }


    /**
    /**
     * Performs a mouse scroll action based on the provided key code.
     * Performs a mouse scroll action based on the provided key code.
@@ -205,12 +221,11 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation
            case DOWN_MOVE_OR_SCROLL -> -1.0f;
            case DOWN_MOVE_OR_SCROLL -> -1.0f;
            default -> 0.0f;
            default -> 0.0f;
        };
        };
        if (mVirtualMouse != null) {
        waitForVirtualMouseCreation();
        mVirtualMouse.sendScrollEvent(new VirtualMouseScrollEvent.Builder()
        mVirtualMouse.sendScrollEvent(new VirtualMouseScrollEvent.Builder()
                .setYAxisMovement(y)
                .setYAxisMovement(y)
                .build()
                .build()
        );
        );
        }
        if (DEBUG) {
        if (DEBUG) {
            Slog.d(LOG_TAG, "Performed mouse key event: " + mouseKeyEvent.name()
            Slog.d(LOG_TAG, "Performed mouse key event: " + mouseKeyEvent.name()
                    + " for scroll action with axis movement (y=" + y + ")");
                    + " for scroll action with axis movement (y=" + y + ")");
+0 −4
Original line number Original line Diff line number Diff line
@@ -45,7 +45,6 @@ import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.MockitoAnnotations
import java.util.concurrent.TimeUnit
import java.util.LinkedList
import java.util.LinkedList
import java.util.Queue
import java.util.Queue
import android.util.ArraySet
import android.util.ArraySet
@@ -117,9 +116,6 @@ class MouseKeysInterceptorTest {
        Mockito.`when`(mockAms.traceManager).thenReturn(mockTraceManager)
        Mockito.`when`(mockAms.traceManager).thenReturn(mockTraceManager)


        mouseKeysInterceptor = MouseKeysInterceptor(mockAms, testLooper.looper, DISPLAY_ID)
        mouseKeysInterceptor = MouseKeysInterceptor(mockAms, testLooper.looper, DISPLAY_ID)
        // VirtualMouse is created on a separate thread.
        // Wait for VirtualMouse to be created before running tests
        TimeUnit.MILLISECONDS.sleep(20L)
        mouseKeysInterceptor.next = nextInterceptor
        mouseKeysInterceptor.next = nextInterceptor
    }
    }