summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/gesture/EdgeGestureInputFilter.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/gesture/EdgeGestureInputFilter.java')
-rw-r--r--services/java/com/android/server/gesture/EdgeGestureInputFilter.java539
1 files changed, 0 insertions, 539 deletions
diff --git a/services/java/com/android/server/gesture/EdgeGestureInputFilter.java b/services/java/com/android/server/gesture/EdgeGestureInputFilter.java
deleted file mode 100644
index c42b7d0..0000000
--- a/services/java/com/android/server/gesture/EdgeGestureInputFilter.java
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- * Copyright (C) 2013 The CyanogenMod Project (Jens Doll)
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.android.server.gesture;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.util.Slog;
-import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.IInputFilter;
-import android.view.IInputFilterHost;
-import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.MotionEvent;
-import android.view.WindowManagerPolicy;
-import android.view.MotionEvent.PointerCoords;
-import android.view.MotionEvent.PointerProperties;
-
-import com.android.internal.R;
-import com.android.internal.util.gesture.EdgeGesturePosition;
-import com.android.server.gesture.EdgeGestureTracker.OnActivationListener;
-
-import java.io.PrintWriter;
-
-/**
- * A simple input filter, that listens for edge swipe gestures in the motion event input
- * stream.
- * <p>
- * There are 5 distinct states of this filter.
- * 1) LISTEN:
- * mTracker.active == false
- * All motion events are passed through. If a ACTION_DOWN within a gesture trigger area happen
- * switch to DETECTING.
- * 2) DETECTING:
- * mTracker.active == true
- * All events are buffered now, and the gesture is checked by mTracker. If mTracker rejects
- * the gesture (hopefully as fast as possible) all cached events will be flushed out and the
- * filter falls back to LISTEN.
- * If mTracker accepts the gesture, clear all cached events and go to LOCKED.
- * 3) LOCKED:
- * mTracker.active == false
- * All events will be cached until the state changes to SYNTHESIZE through a filter
- * unlock event. If there is a ACTION_UP, _CANCEL or any PointerId differently to the last
- * event seen when mTracker accepted the gesture, we flush all events and go to LISTEN.
- * 4) SYNTHESIZE:
- * The first motion event found will be turned into a ACTION_DOWN event, all previous events
- * will be discarded.
- * 5) POSTSYNTHESIZE:
- * mSyntheticDownTime != -1
- * All following events will have the down time set to the synthesized ACTION_DOWN event time
- * until an ACTION_UP or ACTION_CANCEL is encountered and the state is reset to LISTEN.
- * 6) DROP:
- * All following events will be discarded. If there is an ACTION_UP, _CANCEL
- * we go to LISTEN state.
- * <p>
- * If you are reading this within Java Doc, you are doing something wrong ;)
- */
-public class EdgeGestureInputFilter implements IInputFilter {
- /* WARNING!! The IInputFilter interface is used directly, there is no Binder between this and
- * the InputDispatcher.
- * This is fine, because it prevents unnecessary parceling, but beware:
- * This means we are running on the dispatch or listener thread of the input dispatcher. Every
- * cycle we waste here adds to the overall input latency.
- */
- private static final String TAG = "EdgeGestureInputFilter";
- private static final boolean DEBUG = false;
- private static final boolean DEBUG_INPUT = false;
- // TODO: Should be turned off in final commit
- private static final boolean SYSTRACE = false;
-
- private final Handler mHandler;
-
- private IInputFilterHost mHost = null; // dispatcher thread
-
- private static final class MotionEventInfo {
- private static final int MAX_POOL_SIZE = 16;
-
- private static final Object sLock = new Object();
- private static MotionEventInfo sPool;
- private static int sPoolSize;
-
- private boolean mInPool;
-
- public static MotionEventInfo obtain(MotionEvent event, int policyFlags) {
- synchronized (sLock) {
- MotionEventInfo info;
- if (sPoolSize > 0) {
- sPoolSize--;
- info = sPool;
- sPool = info.next;
- info.next = null;
- info.mInPool = false;
- } else {
- info = new MotionEventInfo();
- }
- info.initialize(event, policyFlags);
- return info;
- }
- }
-
- private void initialize(MotionEvent event, int policyFlags) {
- this.event = MotionEvent.obtain(event);
- this.policyFlags = policyFlags;
- cachedTimeMillis = SystemClock.uptimeMillis();
- }
-
- public void recycle() {
- synchronized (sLock) {
- if (mInPool) {
- throw new IllegalStateException("Already recycled.");
- }
- clear();
- if (sPoolSize < MAX_POOL_SIZE) {
- sPoolSize++;
- next = sPool;
- sPool = this;
- mInPool = true;
- }
- }
- }
-
- private void clear() {
- event.recycle();
- event = null;
- policyFlags = 0;
- }
-
- public MotionEventInfo next;
- public MotionEvent event;
- public int policyFlags;
- public long cachedTimeMillis;
- }
- private final Object mLock = new Object();
- private MotionEventInfo mMotionEventQueue; // guarded by mLock
- private MotionEventInfo mMotionEventQueueTail; // guarded by mLock
- /* DEBUG */
- private int mMotionEventQueueCountDebug; // guarded by mLock
-
- private int mDeviceId; // dispatcher only
- private enum State {
- LISTEN, DETECTING, LOCKED, SYNTHESIZE, POSTSYNTHESIZE, DROP;
- }
- private State mState = State.LISTEN; // guarded by mLock
- private EdgeGestureTracker mTracker; // guarded by mLock
- private volatile int mPositions; // written by handler / read by dispatcher
- private volatile int mSensitivity; // written by handler / read by dispatcher
-
- // only used by dispatcher
- private long mSyntheticDownTime = -1;
- private PointerCoords[] mTempPointerCoords = new PointerCoords[1];
- private PointerProperties[] mTempPointerProperties = new PointerProperties[1];
-
- public EdgeGestureInputFilter(Context context, Handler handler) {
- mHandler = handler;
-
- final Resources res = context.getResources();
- mTracker = new EdgeGestureTracker(res.getDimensionPixelSize(
- R.dimen.edge_gesture_trigger_thickness),
- res.getDimensionPixelSize(R.dimen.edge_gesture_trigger_distance),
- res.getDimensionPixelSize(R.dimen.edge_gesture_perpendicular_distance));
- mTracker.setOnActivationListener(new OnActivationListener() {
- public void onActivation(MotionEvent event, int touchX, int touchY, EdgeGesturePosition position) {
- // mLock is held by #processMotionEvent
- mHandler.obtainMessage(EdgeGestureService.MSG_EDGE_GESTURE_ACTIVATION,
- touchX, touchY, position).sendToTarget();
- mState = State.LOCKED;
- }
- });
- mTempPointerCoords[0] = new PointerCoords();
- mTempPointerProperties[0] = new PointerProperties();
- }
-
- // called from handler thread (lock taken)
- public void updateDisplay(Display display, DisplayInfo displayInfo) {
- synchronized (mLock) {
- mTracker.updateDisplay(display);
- }
- }
-
- // called from handler thread (lock taken)
- public void updatePositions(int positions) {
- mPositions = positions;
- }
-
- // called from handler thread (lock taken)
- public void updateSensitivity(int sensitivity) {
- mSensitivity = sensitivity;
- }
-
- // called from handler thread
- public boolean unlockFilter() {
- synchronized (mLock) {
- if (mState == State.LOCKED) {
- mState = State.SYNTHESIZE;
- return true;
- }
- }
- return false;
- }
-
- public boolean dropSequence() {
- synchronized (mLock) {
- if (mState == State.LOCKED) {
- mState = State.DROP;
- return true;
- }
- }
- return false;
- }
-
- /**
- * Called to enqueue the input event for filtering.
- * The event must be recycled after the input filter processed it.
- * This method is guaranteed to be non-reentrant.
- *
- * @see InputFilter#filterInputEvent(InputEvent, int)
- * @param event The input event to enqueue.
- */
- // called by the input dispatcher thread
- public void filterInputEvent(InputEvent event, int policyFlags) throws RemoteException {
- if (SYSTRACE) {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "filterInputEvent");
- }
- try {
- if (((event.getSource() & InputDevice.SOURCE_TOUCHSCREEN)
- != InputDevice.SOURCE_TOUCHSCREEN)
- || !(event instanceof MotionEvent)) {
- sendInputEvent(event, policyFlags);
- return;
- }
- if (DEBUG_INPUT) {
- Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
- + Integer.toHexString(policyFlags));
- }
- MotionEvent motionEvent = (MotionEvent) event;
- final int deviceId = event.getDeviceId();
- if (deviceId != mDeviceId) {
- processDeviceSwitch(deviceId, motionEvent, policyFlags);
- } else {
- if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
- synchronized (mLock) {
- clearAndResetStateLocked(false, true);
- }
- }
- processMotionEvent(motionEvent, policyFlags);
- }
- } finally {
- event.recycle();
- if (SYSTRACE) {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- }
- }
-
- private void processDeviceSwitch(int deviceId, MotionEvent motionEvent, int policyFlags) {
- if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mDeviceId = deviceId;
- synchronized (mLock) {
- clearAndResetStateLocked(true, false);
- processMotionEvent(motionEvent, policyFlags);
- }
- } else {
- sendInputEvent(motionEvent, policyFlags);
- }
- }
-
- private void processMotionEvent(MotionEvent motionEvent, int policyFlags) {
- final int action = motionEvent.getActionMasked();
-
- synchronized (mLock) {
- switch (mState) {
- case LISTEN:
- if (action == MotionEvent.ACTION_DOWN) {
- boolean hit = mPositions != 0
- && mTracker.start(motionEvent, mPositions, mSensitivity);
- if (DEBUG) Slog.d(TAG, "start:" + hit);
- if (hit) {
- // cache the down event
- cacheDelayedMotionEventLocked(motionEvent, policyFlags);
- mState = State.DETECTING;
- return;
- }
- }
- sendInputEvent(motionEvent, policyFlags);
- break;
- case DETECTING:
- cacheDelayedMotionEventLocked(motionEvent, policyFlags);
- if (action == MotionEvent.ACTION_MOVE) {
- if (mTracker.move(motionEvent)) {
- // return: the tracker is either detecting or triggered onActivation
- return;
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "move: reset!");
- }
- clearAndResetStateLocked(false, true);
- break;
- case LOCKED:
- cacheDelayedMotionEventLocked(motionEvent, policyFlags);
- if (action != MotionEvent.ACTION_MOVE) {
- clearAndResetStateLocked(false, true);
- }
- break;
- case SYNTHESIZE:
- if (action == MotionEvent.ACTION_MOVE) {
- clearDelayedMotionEventsLocked();
- sendSynthesizedMotionEventLocked(motionEvent, policyFlags);
- mState = State.POSTSYNTHESIZE;
- } else {
- // This is the case where a race condition caught us: We already
- // returned the handler thread that it is all right to call
- // #gainTouchFocus(), but apparently this was wrong, as the gesture
- // was canceled now.
- clearAndResetStateLocked(false, true);
- }
- break;
- case POSTSYNTHESIZE:
- motionEvent.setDownTime(mSyntheticDownTime);
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- mState = State.LISTEN;
- mSyntheticDownTime = -1;
- }
- sendInputEvent(motionEvent, policyFlags);
- break;
- case DROP:
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- clearDelayedMotionEventsLocked();
- mState = State.LISTEN;
- }
- break;
- }
- }
- }
-
- private void clearAndResetStateLocked(boolean force, boolean shift) {
- // ignore soft reset in POSTSYNTHESIZE, because we need to tamper with
- // the event stream and going to LISTEN after an ACTION_UP anyway
- if (!force && (mState == State.POSTSYNTHESIZE)) {
- return;
- }
- switch (mState) {
- case LISTEN:
- // this is a nop
- break;
- case DETECTING:
- mTracker.reset();
- // intentionally no break here
- case LOCKED:
- case SYNTHESIZE:
- sendDelayedMotionEventsLocked(shift);
- break;
- case POSTSYNTHESIZE:
- // hard reset (this will break the event stream)
- Slog.w(TAG, "Quit POSTSYNTHESIZE without ACTION_UP from ACTION_DOWN at "
- + mSyntheticDownTime);
- mSyntheticDownTime = -1;
- break;
- }
- // if there are future events that need to be tampered with, goto POSTSYNTHESIZE
- mState = mSyntheticDownTime == -1 ? State.LISTEN : State.POSTSYNTHESIZE;
- }
-
- private void sendInputEvent(InputEvent event, int policyFlags) {
- try {
- mHost.sendInputEvent(event, policyFlags);
- } catch (RemoteException e) {
- /* ignore */
- }
- }
-
- private void cacheDelayedMotionEventLocked(MotionEvent event, int policyFlags) {
- MotionEventInfo info = MotionEventInfo.obtain(event, policyFlags);
- if (mMotionEventQueue == null) {
- mMotionEventQueue = info;
- } else {
- mMotionEventQueueTail.next = info;
- }
- mMotionEventQueueTail = info;
- mMotionEventQueueCountDebug++;
- if (SYSTRACE) {
- Trace.traceCounter(Trace.TRACE_TAG_INPUT, "meq", mMotionEventQueueCountDebug);
- }
- }
-
- private void sendDelayedMotionEventsLocked(boolean shift) {
- while (mMotionEventQueue != null) {
- MotionEventInfo info = mMotionEventQueue;
- mMotionEventQueue = info.next;
-
- if (DEBUG) {
- Slog.d(TAG, "Replay event: " + info.event);
- }
- mMotionEventQueueCountDebug--;
- if (SYSTRACE) {
- Trace.traceCounter(Trace.TRACE_TAG_INPUT, "meq", mMotionEventQueueCountDebug);
- }
- if (shift) {
- final long offset = SystemClock.uptimeMillis() - info.cachedTimeMillis;
- if (info.event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mSyntheticDownTime = info.event.getDownTime() + offset;
- }
- sendMotionEventWithOffsetLocked(info.event, info.policyFlags, mSyntheticDownTime, offset);
- if (info.event.getActionMasked() == MotionEvent.ACTION_UP) {
- mSyntheticDownTime = -1;
- }
- } else {
- sendInputEvent(info.event, info.policyFlags);
- }
- info.recycle();
- }
- mMotionEventQueueTail = null;
- }
-
- private void clearDelayedMotionEventsLocked() {
- while (mMotionEventQueue != null) {
- MotionEventInfo next = mMotionEventQueue.next;
- mMotionEventQueue.recycle();
- mMotionEventQueue = next;
- }
- mMotionEventQueueTail = null;
- mMotionEventQueueCountDebug = 0;
- if (SYSTRACE) {
- Trace.traceCounter(Trace.TRACE_TAG_INPUT, "meq", mMotionEventQueueCountDebug);
- }
- }
-
- private void sendMotionEventWithOffsetLocked(MotionEvent event, int policyFlags,
- long downTime, long offset) {
- final int pointerCount = event.getPointerCount();
- PointerCoords[] coords = getTempPointerCoordsWithMinSizeLocked(pointerCount);
- PointerProperties[] properties = getTempPointerPropertiesWithMinSizeLocked(pointerCount);
- for (int i = 0; i < pointerCount; i++) {
- event.getPointerCoords(i, coords[i]);
- event.getPointerProperties(i, properties[i]);
- }
- final long eventTime = event.getEventTime() + offset;
- sendInputEvent(MotionEvent.obtain(downTime, eventTime, event.getAction(), pointerCount,
- properties, coords, event.getMetaState(), event.getButtonState(), 1.0f, 1.0f,
- event.getDeviceId(), event.getEdgeFlags(), event.getSource(), event.getFlags()),
- policyFlags);
- }
-
- private PointerCoords[] getTempPointerCoordsWithMinSizeLocked(int size) {
- final int oldSize = mTempPointerCoords.length;
- if (oldSize < size) {
- PointerCoords[] oldTempPointerCoords = mTempPointerCoords;
- mTempPointerCoords = new PointerCoords[size];
- System.arraycopy(oldTempPointerCoords, 0, mTempPointerCoords, 0, oldSize);
- }
- for (int i = oldSize; i < size; i++) {
- mTempPointerCoords[i] = new PointerCoords();
- }
- return mTempPointerCoords;
- }
-
- private PointerProperties[] getTempPointerPropertiesWithMinSizeLocked(int size) {
- final int oldSize = mTempPointerProperties.length;
- if (oldSize < size) {
- PointerProperties[] oldTempPointerProperties = mTempPointerProperties;
- mTempPointerProperties = new PointerProperties[size];
- System.arraycopy(oldTempPointerProperties, 0, mTempPointerProperties, 0, oldSize);
- }
- for (int i = oldSize; i < size; i++) {
- mTempPointerProperties[i] = new PointerProperties();
- }
- return mTempPointerProperties;
- }
-
- private void sendSynthesizedMotionEventLocked(MotionEvent event, int policyFlags) {
- if (event.getPointerCount() == 1) {
- event.getPointerCoords(0, mTempPointerCoords[0]);
- event.getPointerProperties(0, mTempPointerProperties[0]);
- MotionEvent down = MotionEvent.obtain(event.getEventTime(), event.getEventTime(),
- MotionEvent.ACTION_DOWN, 1, mTempPointerProperties, mTempPointerCoords,
- event.getMetaState(), event.getButtonState(),
- 1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
- event.getSource(), event.getFlags());
- Slog.d(TAG, "Synthesized event:" + down);
- sendInputEvent(down, policyFlags);
- mSyntheticDownTime = event.getEventTime();
- } else {
- Slog.w(TAG, "Could not synthesize MotionEvent, this will drop all following events!");
- }
- }
-
- // should never be called
- public IBinder asBinder() {
- throw new UnsupportedOperationException();
- }
-
- // called by the input dispatcher thread
- public void install(IInputFilterHost host) throws RemoteException {
- if (DEBUG) {
- Slog.d(TAG, "EdgeGesture input filter installed.");
- }
- mHost = host;
- synchronized (mLock) {
- clearAndResetStateLocked(true, false);
- }
- }
-
- // called by the input dispatcher thread
- public void uninstall() throws RemoteException {
- if (DEBUG) {
- Slog.d(TAG, "EdgeGesture input filter uninstalled.");
- }
- }
-
- // called by a Binder thread
- public void dump(PrintWriter pw, String prefix) {
- synchronized (mLock) {
- pw.print(prefix);
- pw.println("mState=" + mState.name());
- pw.print(prefix);
- pw.println("mPositions=0x" + Integer.toHexString(mPositions));
- pw.print(prefix);
- pw.println("mQueue=" + mMotionEventQueueCountDebug + " items");
- }
- }
-}