diff options
| author | Jens Doll <jens.doll@gmail.com> | 2013-04-17 17:32:04 +0200 |
|---|---|---|
| committer | Danny Baumann <dannybaumann@web.de> | 2013-05-15 13:44:23 +0200 |
| commit | 9f11bd11a1389bb98d54f6e6114028f4e39f90e1 (patch) | |
| tree | 9ca53656e66ccc069f452ed8f7544f6aaf6613cf /services/java/com/android/server/input/InputManagerService.java | |
| parent | cdd98977f5089eb829724579943dbe23480ea272 (diff) | |
| download | frameworks_base-9f11bd11a1389bb98d54f6e6114028f4e39f90e1.zip frameworks_base-9f11bd11a1389bb98d54f6e6114028f4e39f90e1.tar.gz frameworks_base-9f11bd11a1389bb98d54f6e6114028f4e39f90e1.tar.bz2 | |
Pie controls: Introducing a pie delivery service
To make pie controls more reliable, it is neccessary to detect
trigger actions directly from the input stream.
This commit introduces a new system service, that filters all
input events in front of the input dispatcher to detect pie activations.
This commit introduces:
* A new system server local API in the input manager service to register
secondary input filters. These filters are behind the default
accessibility filter but before the input event dispatching of
the android framework.
* A new system service, that binds to the new API to listen for pie
activation gestures.
* A non-public manager class providing access to the newly created pie
service.
The service manager name of the service is "pieservice". The non-public
AIDL interface of the service is IPieService.aidl. To register a new
pie activation listener the INJECT_INPUT permission is needed. The
service state can be dumped by the "dumpsys pieservice" command.
Note: This commit only introduces the pie service. There is another
commit, that binds the actual pie controls to the pie service.
Patch Set #1:
* The pie service is currently disabled by default and needs to be
enabled by device overlays (see config.xml / config_allowPieService).
Patch Set #2:
* Activation fixes
* Debug dump improvements
Patch Set #4:
* Added systrace support (TRACE_INPUT_TAG)
* Switch default to enable service on all devices.
* Moved Position to com.internal.android.utils.pie.*
* Some more code rearrangements
Patch Set #5:
* Rebase
Patch Set #6:
* Cover more corner cases on PieInputFilter
* Adjust gesture time out
Patch Set #7:
* Do not send events that are from the past
* Recycle all events
Patch Set #8:
* Handle binder died events in PieService correctly
Patch Set #10:
* Simplified locking
* SYSTEM_UI_FLAG_HIDE_NAVIGATION support
* Fixed ADW Lauchner bug
Change-Id: I6a4a4635bed420e800a3230457ee690131116a11
Diffstat (limited to 'services/java/com/android/server/input/InputManagerService.java')
| -rw-r--r-- | services/java/com/android/server/input/InputManagerService.java | 136 |
1 files changed, 109 insertions, 27 deletions
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java index 9921b5c..aa3b283 100644 --- a/services/java/com/android/server/input/InputManagerService.java +++ b/services/java/com/android/server/input/InputManagerService.java @@ -97,6 +97,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor, DisplayManagerService.InputManagerFuncs { static final String TAG = "InputManager"; static final boolean DEBUG = false; + static final boolean DEBUG_FILTER = false; private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; @@ -143,8 +144,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 int nativeInit(InputManagerService service, Context context, MessageQueue messageQueue); @@ -475,34 +477,77 @@ public class InputManagerService extends IInputManager.Stub */ public void setInputFilter(IInputFilter filter) { synchronized (mInputFilterLock) { - final IInputFilter oldFilter = mInputFilter; + final IInputFilter oldFilter = mInputFilterHost != null + ? mInputFilterHost.mInputFilter : null; if (oldFilter == filter) { return; // nothing to do } if (oldFilter != null) { - mInputFilter = null; mInputFilterHost.disconnectLocked(); + mInputFilterChain.remove(mInputFilterHost); mInputFilterHost = null; - try { - oldFilter.uninstall(); - } catch (RemoteException re) { - /* ignore */ - } } 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()); + } + } + + 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, filter != null); + 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 @@ -1327,16 +1372,23 @@ public class InputManagerService extends IInputManager.Stub // Native callback. final boolean filterInputEvent(InputEvent event, int policyFlags) { + ChainedInputFilterHost head = null; synchronized (mInputFilterLock) { - if (mInputFilter != null) { - try { - mInputFilter.filterInputEvent(event, policyFlags); - } catch (RemoteException e) { - /* ignore */ - } - return false; + 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 { + head.mInputFilter.filterInputEvent(event, policyFlags); + } catch (RemoteException e) { + /* ignore */ + } + return false; + } event.recycle(); return true; } @@ -1561,10 +1613,32 @@ public class InputManagerService extends IInputManager.Stub /** * Hosting interface for input filters to call back into the input manager. */ - private final class InputFilterHost extends IInputFilterHost.Stub { + 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; } @@ -1576,9 +1650,17 @@ public class InputManagerService extends IInputManager.Stub synchronized (mInputFilterLock) { if (!mDisconnected) { - nativeInjectInputEvent(mPtr, event, 0, 0, - InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0, - policyFlags | WindowManagerPolicy.FLAG_FILTERED); + if (mNext == null) { + nativeInjectInputEvent(mPtr, event, 0, 0, + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0, + policyFlags | WindowManagerPolicy.FLAG_FILTERED); + } else { + try { + mNext.mInputFilter.filterInputEvent(event, policyFlags); + } catch (RemoteException e) { + /* ignore */ + } + } } } } |
