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

Commit 4f8045a2 authored by Steve Kondik's avatar Steve Kondik Committed by Steve Kondik
Browse files

input: Forward-port support for chained input filters

 * Original code by Jens Doll for Pie controls
 * Needed now to support the gesture sensor

Change-Id: I0b86d70f9a040ba5953e1820e9818df0f10091ee
parent 8fcaa972
Loading
Loading
Loading
Loading
+132 −16
Original line number Diff line number Diff line
@@ -151,7 +151,9 @@ public class InputManagerService extends IInputManager.Stub
    // State for the currently installed input filter.
    final Object mInputFilterLock = new Object();
    IInputFilter mInputFilter; // guarded by mInputFilterLock
    InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
    ChainedInputFilterHost mInputFilterHost; // guarded by mInputFilterLock
    ArrayList<ChainedInputFilterHost> mInputFilterChain =
            new ArrayList<ChainedInputFilterHost>(); // guarded by mInputFilterLock

    private static native long nativeInit(InputManagerService service,
            Context context, MessageQueue messageQueue);
@@ -501,8 +503,8 @@ public class InputManagerService extends IInputManager.Stub
            }

            if (oldFilter != null) {
                mInputFilter = null;
                mInputFilterHost.disconnectLocked();
                mInputFilterChain.remove(mInputFilterHost);
                mInputFilterHost = null;
                try {
                    oldFilter.uninstall();
@@ -512,17 +514,64 @@ public class InputManagerService extends IInputManager.Stub
            }

            if (filter != null) {
                mInputFilter = filter;
                mInputFilterHost = new InputFilterHost();
                try {
                    filter.install(mInputFilterHost);
                } catch (RemoteException re) {
                    /* ignore */
                ChainedInputFilterHost head = mInputFilterChain.isEmpty() ? null :
                    mInputFilterChain.get(0);
                mInputFilterHost = new ChainedInputFilterHost(filter, head);
                mInputFilterHost.connectLocked();
                mInputFilterChain.add(0, mInputFilterHost);
            }

            nativeSetInputFilterEnabled(mPtr, !mInputFilterChain.isEmpty());
        }
    }

    /**
     * Registers a secondary input filter. These filters are always behind the "original"
     * input filter. This ensures that all input events will be filtered by the
     * {@code AccessibilityManagerService} first.
     * <p>
     * <b>Note:</b> Even though this implementation using AIDL interfaces, it is designed to only
     * provide direct access. Therefore, any filter registering should reside in the
     * system server DVM only!
     *
     * @param filter The input filter to register.
     */
    public void registerSecondaryInputFilter(IInputFilter filter) {
        synchronized (mInputFilterLock) {
            ChainedInputFilterHost host = new ChainedInputFilterHost(filter, null);
            if (!mInputFilterChain.isEmpty()) {
                mInputFilterChain.get(mInputFilterChain.size() - 1).mNext = host;
            }
            host.connectLocked();
            mInputFilterChain.add(host);

            nativeSetInputFilterEnabled(mPtr, !mInputFilterChain.isEmpty());
        }
    }

            nativeSetInputFilterEnabled(mPtr, filter != null);
    public void unregisterSecondaryInputFilter(IInputFilter filter) {
        synchronized (mInputFilterLock) {
            int index = findInputFilterIndexLocked(filter);
            if (index >= 0) {
                ChainedInputFilterHost host = mInputFilterChain.get(index);
                host.disconnectLocked();
                if (index >= 1) {
                    mInputFilterChain.get(index - 1).mNext = host.mNext;
                }
                mInputFilterChain.remove(index);
            }

            nativeSetInputFilterEnabled(mPtr, !mInputFilterChain.isEmpty());
        }
    }

    private int findInputFilterIndexLocked(IInputFilter filter) {
        for (int i = 0; i < mInputFilterChain.size(); i++) {
            if (mInputFilterChain.get(i).mInputFilter == filter) {
                return i;
            }
        }
        return -1;
    }

    @Override // Binder call
@@ -1435,16 +1484,23 @@ public class InputManagerService extends IInputManager.Stub

    // Native callback.
    final boolean filterInputEvent(InputEvent event, int policyFlags) {
        ChainedInputFilterHost head = null;
        synchronized (mInputFilterLock) {
            if (mInputFilter != null) {
            if (!mInputFilterChain.isEmpty()) {
                head = mInputFilterChain.get(0);
            }
        }
        // call filter input event outside of the lock.
        // this is safe, because we know that mInputFilter never changes.
        // we may loose a event, but this does not differ from the original implementation.
        if (head != null) {
            try {
                    mInputFilter.filterInputEvent(event, policyFlags);
                head.mInputFilter.filterInputEvent(event, policyFlags);
            } catch (RemoteException e) {
                /* ignore */
            }
            return false;
        }
        }
        event.recycle();
        return true;
    }
@@ -1694,6 +1750,66 @@ public class InputManagerService extends IInputManager.Stub
        }
    }

    /**
     * Hosting interface for input filters to call back into the input manager.
     */
    private final class ChainedInputFilterHost extends IInputFilterHost.Stub {
        private final IInputFilter mInputFilter;
        private ChainedInputFilterHost mNext;
        private boolean mDisconnected;

        private ChainedInputFilterHost(IInputFilter filter, ChainedInputFilterHost next) {
            mInputFilter = filter;
            mNext = next;
            mDisconnected = false;
        }

        public void connectLocked() {
            try {
                mInputFilter.install(this);
            } catch (RemoteException re) {
                /* ignore */
            }
        }

        public void disconnectLocked() {
            try {
                mInputFilter.uninstall();
            } catch (RemoteException re) {
                /* ignore */
            }
            // DO NOT set mInputFilter to null here! mInputFilter is used outside of the lock!
            mDisconnected = true;
        }

        @Override
        public void sendInputEvent(InputEvent event, int policyFlags) {
            if (event == null) {
                throw new IllegalArgumentException("event must not be null");
            }

            synchronized (mInputFilterLock) {
                if (!mDisconnected) {
                    if (mNext == null) {
                        nativeInjectInputEvent(mPtr, event, Display.DEFAULT_DISPLAY, 0, 0,
                                InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0,
                                policyFlags | WindowManagerPolicy.FLAG_FILTERED);
                    } else {
                        try {
                            // We need to pass a copy into filterInputEvent as it assumes
                            // the callee takes responsibility and recycles it - in case
                            // multiple filters are chained, calling into the second filter
                            // will cause event to be recycled twice
                            mNext.mInputFilter.filterInputEvent(event.copy(), policyFlags);
                        } catch (RemoteException e) {
                            /* ignore */
                        }
                    }
                }
            }
        }
    }

    private static final class KeyboardLayoutDescriptor {
        public String packageName;
        public String receiverName;