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

Commit 9133bfab authored by Zixuan Qu's avatar Zixuan Qu
Browse files

Move device specific logic to VirtualInputDevice and its subclasses.

Only keep virtual input device setup code and move device specific logic
to VirtualInputDevice and its subclasses. The benefits are threefold:

1) Better encapsulation: we used to have all input event injection logic
   in inputController JNI code, it's hard to tell the capability of each
   device type. For example, writeKeyEvent() doesn't have any indication
   of which device it's writing into. If a fd representing a mouse is
   passed in then the key event might be written to a mouse device.

2) No need to manually close the file descripters. We can now depend on
   the destructor of unique_fd.

3) We can get rid of the errorprone static map introduced in ag/21078609
   which keeps record of active pointers on touchscreen.

Test: atest VirtualInputTest VirtualMouseTest VirtualKeyboardTest
VirtualTouchscreenTest VirtualDpadTest InputControllerTest

Bug: 267515782

Change-Id: Ic5f246544172a431090f427457265bbf8d63bd70
parent b835c42f
Loading
Loading
Loading
Loading
+57 −52
Original line number Diff line number Diff line
@@ -242,7 +242,7 @@ class InputController {
    private void closeInputDeviceDescriptorLocked(IBinder token,
            InputDeviceDescriptor inputDeviceDescriptor) {
        token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
        mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
        mNativeWrapper.closeUinput(inputDeviceDescriptor.getNativePointer());
        String phys = inputDeviceDescriptor.getPhys();
        InputManager.getInstance().removeUniqueIdAssociation(phys);
        // Type associations are added in the case of navigation touchpads. Those should be removed
@@ -366,7 +366,7 @@ class InputController {
                throw new IllegalArgumentException(
                        "Could not send key event to input device for given token");
            }
            return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getFileDescriptor(),
            return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getNativePointer(),
                    event.getKeyCode(), event.getAction());
        }
    }
@@ -379,7 +379,7 @@ class InputController {
                throw new IllegalArgumentException(
                        "Could not send key event to input device for given token");
            }
            return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getFileDescriptor(),
            return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getNativePointer(),
                    event.getKeyCode(), event.getAction());
        }
    }
@@ -397,7 +397,7 @@ class InputController {
                throw new IllegalStateException(
                        "Display id associated with this mouse is not currently targetable");
            }
            return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getFileDescriptor(),
            return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getNativePointer(),
                    event.getButtonCode(), event.getAction());
        }
    }
@@ -410,7 +410,7 @@ class InputController {
                throw new IllegalArgumentException(
                        "Could not send touch event to input device for given token");
            }
            return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getFileDescriptor(),
            return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getNativePointer(),
                    event.getPointerId(), event.getToolType(), event.getAction(), event.getX(),
                    event.getY(), event.getPressure(), event.getMajorAxisSize());
        }
@@ -429,7 +429,7 @@ class InputController {
                throw new IllegalStateException(
                        "Display id associated with this mouse is not currently targetable");
            }
            return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getFileDescriptor(),
            return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getNativePointer(),
                    event.getRelativeX(), event.getRelativeY());
        }
    }
@@ -447,7 +447,7 @@ class InputController {
                throw new IllegalStateException(
                        "Display id associated with this mouse is not currently targetable");
            }
            return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getFileDescriptor(),
            return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getNativePointer(),
                    event.getXAxisMovement(), event.getYAxisMovement());
        }
    }
@@ -474,7 +474,7 @@ class InputController {
        synchronized (mLock) {
            fout.println("      Active descriptors: ");
            for (InputDeviceDescriptor inputDeviceDescriptor : mInputDeviceDescriptors.values()) {
                fout.println("        fd: " + inputDeviceDescriptor.getFileDescriptor());
                fout.println("        ptr: " + inputDeviceDescriptor.getNativePointer());
                fout.println("          displayId: " + inputDeviceDescriptor.getDisplayId());
                fout.println("          creationOrder: "
                        + inputDeviceDescriptor.getCreationOrderNumber());
@@ -487,10 +487,10 @@ class InputController {
    }

    @VisibleForTesting
    void addDeviceForTesting(IBinder deviceToken, int fd, int type, int displayId, String phys,
    void addDeviceForTesting(IBinder deviceToken, long ptr, int type, int displayId, String phys,
            String deviceName, int inputDeviceId) {
        synchronized (mLock) {
            mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(fd, () -> {
            mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(ptr, () -> {
            }, type, displayId, phys, deviceName, inputDeviceId));
        }
    }
@@ -504,75 +504,76 @@ class InputController {
        return inputDeviceDescriptors;
    }

    private static native int nativeOpenUinputDpad(String deviceName, int vendorId, int productId,
    private static native long nativeOpenUinputDpad(String deviceName, int vendorId, int productId,
            String phys);
    private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId,
    private static native long nativeOpenUinputKeyboard(String deviceName, int vendorId,
            int productId, String phys);
    private static native int nativeOpenUinputMouse(String deviceName, int vendorId, int productId,
    private static native long nativeOpenUinputMouse(String deviceName, int vendorId, int productId,
            String phys);
    private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId,
    private static native long nativeOpenUinputTouchscreen(String deviceName, int vendorId,
            int productId, String phys, int height, int width);
    private static native boolean nativeCloseUinput(int fd);
    private static native boolean nativeWriteDpadKeyEvent(int fd, int androidKeyCode, int action);
    private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action);
    private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action);
    private static native boolean nativeWriteTouchEvent(int fd, int pointerId, int toolType,
    private static native void nativeCloseUinput(long ptr);
    private static native boolean nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action);
    private static native boolean nativeWriteKeyEvent(long ptr, int androidKeyCode, int action);
    private static native boolean nativeWriteButtonEvent(long ptr, int buttonCode, int action);
    private static native boolean nativeWriteTouchEvent(long ptr, int pointerId, int toolType,
            int action, float locationX, float locationY, float pressure, float majorAxisSize);
    private static native boolean nativeWriteRelativeEvent(int fd, float relativeX,
    private static native boolean nativeWriteRelativeEvent(long ptr, float relativeX,
            float relativeY);
    private static native boolean nativeWriteScrollEvent(int fd, float xAxisMovement,
    private static native boolean nativeWriteScrollEvent(long ptr, float xAxisMovement,
            float yAxisMovement);

    /** Wrapper around the static native methods for tests. */
    @VisibleForTesting
    protected static class NativeWrapper {
        public int openUinputDpad(String deviceName, int vendorId, int productId, String phys) {
        public long openUinputDpad(String deviceName, int vendorId, int productId, String phys) {
            return nativeOpenUinputDpad(deviceName, vendorId, productId, phys);
        }

        public int openUinputKeyboard(String deviceName, int vendorId, int productId, String phys) {
        public long openUinputKeyboard(String deviceName, int vendorId, int productId,
                String phys) {
            return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys);
        }

        public int openUinputMouse(String deviceName, int vendorId, int productId, String phys) {
        public long openUinputMouse(String deviceName, int vendorId, int productId, String phys) {
            return nativeOpenUinputMouse(deviceName, vendorId, productId, phys);
        }

        public int openUinputTouchscreen(String deviceName, int vendorId,
        public long openUinputTouchscreen(String deviceName, int vendorId,
                int productId, String phys, int height, int width) {
            return nativeOpenUinputTouchscreen(deviceName, vendorId, productId, phys, height,
                    width);
        }

        public boolean closeUinput(int fd) {
            return nativeCloseUinput(fd);
        public void closeUinput(long ptr) {
            nativeCloseUinput(ptr);
        }

        public boolean writeDpadKeyEvent(int fd, int androidKeyCode, int action) {
            return nativeWriteDpadKeyEvent(fd, androidKeyCode, action);
        public boolean writeDpadKeyEvent(long ptr, int androidKeyCode, int action) {
            return nativeWriteDpadKeyEvent(ptr, androidKeyCode, action);
        }

        public boolean writeKeyEvent(int fd, int androidKeyCode, int action) {
            return nativeWriteKeyEvent(fd, androidKeyCode, action);
        public boolean writeKeyEvent(long ptr, int androidKeyCode, int action) {
            return nativeWriteKeyEvent(ptr, androidKeyCode, action);
        }

        public boolean writeButtonEvent(int fd, int buttonCode, int action) {
            return nativeWriteButtonEvent(fd, buttonCode, action);
        public boolean writeButtonEvent(long ptr, int buttonCode, int action) {
            return nativeWriteButtonEvent(ptr, buttonCode, action);
        }

        public boolean writeTouchEvent(int fd, int pointerId, int toolType, int action,
        public boolean writeTouchEvent(long ptr, int pointerId, int toolType, int action,
                float locationX, float locationY, float pressure, float majorAxisSize) {
            return nativeWriteTouchEvent(fd, pointerId, toolType,
            return nativeWriteTouchEvent(ptr, pointerId, toolType,
                    action, locationX, locationY,
                    pressure, majorAxisSize);
        }

        public boolean writeRelativeEvent(int fd, float relativeX, float relativeY) {
            return nativeWriteRelativeEvent(fd, relativeX, relativeY);
        public boolean writeRelativeEvent(long ptr, float relativeX, float relativeY) {
            return nativeWriteRelativeEvent(ptr, relativeX, relativeY);
        }

        public boolean writeScrollEvent(int fd, float xAxisMovement, float yAxisMovement) {
            return nativeWriteScrollEvent(fd, xAxisMovement,
        public boolean writeScrollEvent(long ptr, float xAxisMovement, float yAxisMovement) {
            return nativeWriteScrollEvent(ptr, xAxisMovement,
                    yAxisMovement);
        }
    }
@@ -597,7 +598,8 @@ class InputController {

        private static final AtomicLong sNextCreationOrderNumber = new AtomicLong(1);

        private final int mFd;
        // Pointer to the native input device object.
        private final long mPtr;
        private final IBinder.DeathRecipient mDeathRecipient;
        private final @Type int mType;
        private final int mDisplayId;
@@ -611,9 +613,9 @@ class InputController {
        // Monotonically increasing number; devices with lower numbers were created earlier.
        private final long mCreationOrderNumber;

        InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type,
        InputDeviceDescriptor(long ptr, IBinder.DeathRecipient deathRecipient, @Type int type,
                int displayId, String phys, String name, int inputDeviceId) {
            mFd = fd;
            mPtr = ptr;
            mDeathRecipient = deathRecipient;
            mType = type;
            mDisplayId = displayId;
@@ -623,8 +625,8 @@ class InputController {
            mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement();
        }

        public int getFileDescriptor() {
            return mFd;
        public long getNativePointer() {
            return mPtr;
        }

        public int getType() {
@@ -767,7 +769,7 @@ class InputController {
     */
    private void createDeviceInternal(@InputDeviceDescriptor.Type int type, String deviceName,
            int vendorId, int productId, IBinder deviceToken, int displayId, String phys,
            Supplier<Integer> deviceOpener)
            Supplier<Long> deviceOpener)
            throws DeviceCreationException {
        if (!mThreadVerifier.isValidThread()) {
            throw new IllegalStateException(
@@ -776,19 +778,22 @@ class InputController {
        }
        validateDeviceName(deviceName);

        final int fd;
        final long ptr;
        final BinderDeathRecipient binderDeathRecipient;

        final int inputDeviceId;

        setUniqueIdAssociation(displayId, phys);
        try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId)) {
            fd = deviceOpener.get();
            if (fd < 0) {
            ptr = deviceOpener.get();
            // See INVALID_PTR in libs/input/VirtualInputDevice.cpp.
            if (ptr == 0) {
                throw new DeviceCreationException(
                        "A native error occurred when creating touchscreen: " + -fd);
                        "A native error occurred when creating virtual input device: "
                                + deviceName);
            }
            // The fd is valid from here, so ensure that all failures close the fd after this point.
            // The pointer to the native input device is valid from here, so ensure that all
            // failures close the device after this point.
            try {
                inputDeviceId = waiter.waitForDeviceCreation();

@@ -800,7 +805,7 @@ class InputController {
                            "Client died before virtual device could be created.", e);
                }
            } catch (DeviceCreationException e) {
                mNativeWrapper.closeUinput(fd);
                mNativeWrapper.closeUinput(ptr);
                throw e;
            }
        } catch (DeviceCreationException e) {
@@ -810,7 +815,7 @@ class InputController {

        synchronized (mLock) {
            mInputDeviceDescriptors.put(deviceToken,
                    new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys,
                    new InputDeviceDescriptor(ptr, binderDeathRecipient, type, displayId, phys,
                            deviceName, inputDeviceId));
        }
    }
+86 −384

File changed.

Preview size limit exceeded, changes collapsed.

+3 −2
Original line number Diff line number Diff line
@@ -79,7 +79,7 @@ class InputManagerMockHelper {
        InputManager.resetInstance(mIInputManagerMock);
    }

    private Void handleNativeOpenInputDevice(InvocationOnMock inv) {
    private long handleNativeOpenInputDevice(InvocationOnMock inv) {
        Objects.requireNonNull(mDevicesChangedListener,
                "InputController did not register an InputDevicesChangedListener.");

@@ -101,6 +101,7 @@ class InputManagerMockHelper {
        }
        // Process the device added notification.
        mTestableLooper.processAllMessages();
        return null;
        // Return a placeholder pointer to the native input device.
        return 1L;
    }
}