Fixing issues in the TouchExplorer.

1. The up event for a long press was not properly adjusted as the
   long pressing finger may not be on top of the accessibility
   focused item.

2. There was a scenario where two finger swipe leads to a crash.
   One finger moves, second finger goes down but no finger moves,
   the first finger goes up, and now the second finger moves. All
   this has to happen before we decided that user is touch exploring.
   Very hard to happen, this is why we could not easily repro the
   crash.

3. We use the two finger vector angle to determine whether the
   user is dragging or not. However, in some cases we were
   unnecessarily waiting too long before performing the check
   and as a result the notification shade on Manta was not
   expandable.

bug:11341530
bug:11189225

Change-Id: Ieea39783444a1c20581f8addfd518d1c11485099
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index a99b58a..43f12eb 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -76,7 +76,7 @@
     private static final int STATE_DELEGATING = 0x00000004;
     private static final int STATE_GESTURE_DETECTING = 0x00000005;
 
-    // The minimum of the cosine between the vectors of two moving
+    // The maximum of the cosine between the vectors of two moving
     // pointers so they can be considered moving in the same direction.
     private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
 
@@ -436,13 +436,19 @@
                         final int pointerIdBits = (1 << pointerId);
                         mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits,
                                 policyFlags);
+                    } else {
+                        // Cache the event until we discern exploration from gesturing.
+                        mSendHoverEnterAndMoveDelayed.addEvent(event);
                     }
-                    // Cache the event until we discern exploration from gesturing.
-                    mSendHoverEnterAndMoveDelayed.addEvent(event);
                 }
             } break;
             case MotionEvent.ACTION_POINTER_DOWN: {
-                /* do nothing - let the code for ACTION_MOVE decide what to do */
+                // Another finger down means that if we have not started to deliver
+                // hover events, we will not have to. The code for ACTION_MOVE will
+                // decide what we will actually do next.
+                mSendHoverEnterAndMoveDelayed.cancel();
+                mSendHoverExitDelayed.cancel();
+                mPerformLongPressDelayed.cancel();
             } break;
             case MotionEvent.ACTION_MOVE: {
                 final int pointerId = receivedTracker.getPrimaryPointerId();
@@ -518,14 +524,11 @@
                                     mPerformLongPressDelayed.cancel();
                                 }
                             }
-                            // The user is either double tapping or performing a long
-                            // press, so do not send move events yet.
-                            if (mDoubleTapDetector.firstTapDetected()) {
-                                break;
+                            if (mTouchExplorationInProgress) {
+                                sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
+                                sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
+                                        policyFlags);
                             }
-                            sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
-                            sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
-                                    policyFlags);
                         }
                     } break;
                     case 2: {
@@ -539,22 +542,24 @@
                             mPerformLongPressDelayed.cancel();
                         } else {
                             mPerformLongPressDelayed.cancel();
-                            // If the user is touch exploring the second pointer may be
-                            // performing a double tap to activate an item without need
-                            // for the user to lift his exploring finger.
-                            // It is *important* to use the distance traveled by the pointers
-                            // on the screen which may or may not be magnified.
-                            final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId)
-                                    - rawEvent.getX(pointerIndex);
-                            final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId)
-                                    - rawEvent.getY(pointerIndex);
-                            final double moveDelta = Math.hypot(deltaX, deltaY);
-                            if (moveDelta < mDoubleTapSlop) {
-                                break;
+                            if (mTouchExplorationInProgress) {
+                                // If the user is touch exploring the second pointer may be
+                                // performing a double tap to activate an item without need
+                                // for the user to lift his exploring finger.
+                                // It is *important* to use the distance traveled by the pointers
+                                // on the screen which may or may not be magnified.
+                                final float deltaX = receivedTracker.getReceivedPointerDownX(
+                                        pointerId) - rawEvent.getX(pointerIndex);
+                                final float deltaY = receivedTracker.getReceivedPointerDownY(
+                                        pointerId) - rawEvent.getY(pointerIndex);
+                                final double moveDelta = Math.hypot(deltaX, deltaY);
+                                if (moveDelta < mDoubleTapSlop) {
+                                    break;
+                                }
+                                // We are sending events so send exit and gesture
+                                // end since we transition to another state.
+                                sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
                             }
-                            // We are sending events so send exit and gesture
-                            // end since we transition to another state.
-                            sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
                         }
 
                         // We know that a new state transition is to happen and the
@@ -736,20 +741,34 @@
                         + "there is at least one pointer down!");
             }
             case MotionEvent.ACTION_UP: {
-                mAms.onTouchInteractionEnd();
+                // Offset the event if we are doing a long press as the
+                // target is not necessarily under the user's finger.
+                if (mLongPressingPointerId >= 0) {
+                    event = offsetEvent(event, - mLongPressingPointerDeltaX,
+                            - mLongPressingPointerDeltaY);
+                    // Clear the long press state.
+                    mLongPressingPointerId = -1;
+                    mLongPressingPointerDeltaX = 0;
+                    mLongPressingPointerDeltaY = 0;
+                }
+
+                // Deliver the event.
+                sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
+
                 // Announce the end of a the touch interaction.
+                mAms.onTouchInteractionEnd();
                 sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
-                mLongPressingPointerId = -1;
-                mLongPressingPointerDeltaX = 0;
-                mLongPressingPointerDeltaY = 0;
+
                 mCurrentState = STATE_TOUCH_EXPLORING;
             } break;
             case MotionEvent.ACTION_CANCEL: {
                 clear(event, policyFlags);
             } break;
+            default: {
+                // Deliver the event.
+                sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
+            }
         }
-        // Deliver the event.
-        sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
     }
 
     private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) {
@@ -959,27 +978,8 @@
         // long press it, or even worse to avoid the user long pressing
         // on the wrong item since click and long press behave differently.
         if (mLongPressingPointerId >= 0) {
-            final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
-            final int pointerCount = event.getPointerCount();
-            PointerProperties[] props = PointerProperties.createArray(pointerCount);
-            PointerCoords[] coords = PointerCoords.createArray(pointerCount);
-            for (int i = 0; i < pointerCount; i++) {
-                event.getPointerProperties(i, props[i]);
-                event.getPointerCoords(i, coords[i]);
-                if (i == remappedIndex) {
-                    coords[i].x -= mLongPressingPointerDeltaX;
-                    coords[i].y -= mLongPressingPointerDeltaY;
-                }
-            }
-            MotionEvent remapped = MotionEvent.obtain(event.getDownTime(),
-                    event.getEventTime(), event.getAction(), event.getPointerCount(),
-                    props, coords, event.getMetaState(), event.getButtonState(),
-                    1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
-                    event.getSource(), event.getFlags());
-            if (event != prototype) {
-                event.recycle();
-            }
-            event = remapped;
+            event = offsetEvent(event, - mLongPressingPointerDeltaX,
+                    - mLongPressingPointerDeltaY);
         }
 
         if (DEBUG) {
@@ -1004,6 +1004,39 @@
     }
 
     /**
+     * Offsets all pointers in the given event by adding the specified X and Y
+     * offsets.
+     *
+     * @param event The event to offset.
+     * @param offsetX The X offset.
+     * @param offsetY The Y offset.
+     * @return An event with the offset pointers or the original event if both
+     *         offsets are zero.
+     */
+    private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) {
+        if (offsetX == 0 && offsetY == 0) {
+            return event;
+        }
+        final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
+        final int pointerCount = event.getPointerCount();
+        PointerProperties[] props = PointerProperties.createArray(pointerCount);
+        PointerCoords[] coords = PointerCoords.createArray(pointerCount);
+        for (int i = 0; i < pointerCount; i++) {
+            event.getPointerProperties(i, props[i]);
+            event.getPointerCoords(i, coords[i]);
+            if (i == remappedIndex) {
+                coords[i].x += offsetX;
+                coords[i].y += offsetY;
+            }
+        }
+        return MotionEvent.obtain(event.getDownTime(),
+                event.getEventTime(), event.getAction(), event.getPointerCount(),
+                props, coords, event.getMetaState(), event.getButtonState(),
+                1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
+                event.getSource(), event.getFlags());
+    }
+
+    /**
      * Computes the action for an injected event based on a masked action
      * and a pointer index.
      *
@@ -1674,7 +1707,6 @@
 
         // Keep track of the last up pointer data.
         private long mLastReceivedUpPointerDownTime;
-        private int mLastReceivedUpPointerId;
         private float mLastReceivedUpPointerDownX;
         private float mLastReceivedUpPointerDownY;
 
@@ -1690,7 +1722,6 @@
             mReceivedPointersDown = 0;
             mPrimaryPointerId = 0;
             mLastReceivedUpPointerDownTime = 0;
-            mLastReceivedUpPointerId = 0;
             mLastReceivedUpPointerDownX = 0;
             mLastReceivedUpPointerDownY = 0;
         }
@@ -1823,7 +1854,6 @@
             final int pointerId = event.getPointerId(pointerIndex);
             final int pointerFlag = (1 << pointerId);
 
-            mLastReceivedUpPointerId = 0;
             mLastReceivedUpPointerDownTime = 0;
             mLastReceivedUpPointerDownX = 0;
             mLastReceivedUpPointerDownX = 0;
@@ -1848,7 +1878,6 @@
             final int pointerId = event.getPointerId(pointerIndex);
             final int pointerFlag = (1 << pointerId);
 
-            mLastReceivedUpPointerId = pointerId;
             mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId);
             mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId];
             mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId];