Merge changes from topic "Four-finger Gestures"

* changes:
  Add four-finger gestures.
  Fix crash when a motion event doesn't include locations for all pointers.
diff --git a/api/current.txt b/api/current.txt
index 81c4c92..d19eb0f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2888,6 +2888,13 @@
     field public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; // 0x20
     field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d
     field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18
+    field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26
+    field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25
+    field public static final int GESTURE_4_FINGER_SWIPE_DOWN = 34; // 0x22
+    field public static final int GESTURE_4_FINGER_SWIPE_LEFT = 35; // 0x23
+    field public static final int GESTURE_4_FINGER_SWIPE_RIGHT = 36; // 0x24
+    field public static final int GESTURE_4_FINGER_SWIPE_UP = 33; // 0x21
+    field public static final int GESTURE_4_FINGER_TRIPLE_TAP = 39; // 0x27
     field public static final int GESTURE_DOUBLE_TAP = 17; // 0x11
     field public static final int GESTURE_DOUBLE_TAP_AND_HOLD = 18; // 0x12
     field public static final int GESTURE_SWIPE_DOWN = 2; // 0x2
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 9cf1de9..ace13513 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -31,6 +31,13 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_UP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_TRIPLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN;
@@ -105,7 +112,14 @@
             GESTURE_3_FINGER_SWIPE_DOWN,
             GESTURE_3_FINGER_SWIPE_LEFT,
             GESTURE_3_FINGER_SWIPE_RIGHT,
-            GESTURE_3_FINGER_SWIPE_UP
+            GESTURE_3_FINGER_SWIPE_UP,
+            GESTURE_4_FINGER_DOUBLE_TAP,
+            GESTURE_4_FINGER_SINGLE_TAP,
+            GESTURE_4_FINGER_SWIPE_DOWN,
+            GESTURE_4_FINGER_SWIPE_LEFT,
+            GESTURE_4_FINGER_SWIPE_RIGHT,
+            GESTURE_4_FINGER_SWIPE_UP,
+            GESTURE_4_FINGER_TRIPLE_TAP
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface GestureId {}
@@ -165,6 +179,9 @@
             case GESTURE_3_FINGER_SINGLE_TAP: return "GESTURE_3_FINGER_SINGLE_TAP";
             case GESTURE_3_FINGER_DOUBLE_TAP: return "GESTURE_3_FINGER_DOUBLE_TAP";
             case GESTURE_3_FINGER_TRIPLE_TAP: return "GESTURE_3_FINGER_TRIPLE_TAP";
+            case GESTURE_4_FINGER_SINGLE_TAP: return "GESTURE_4_FINGER_SINGLE_TAP";
+            case GESTURE_4_FINGER_DOUBLE_TAP: return "GESTURE_4_FINGER_DOUBLE_TAP";
+            case GESTURE_4_FINGER_TRIPLE_TAP: return "GESTURE_4_FINGER_TRIPLE_TAP";
             case GESTURE_DOUBLE_TAP: return "GESTURE_DOUBLE_TAP";
             case GESTURE_DOUBLE_TAP_AND_HOLD: return "GESTURE_DOUBLE_TAP_AND_HOLD";
             case GESTURE_SWIPE_DOWN: return "GESTURE_SWIPE_DOWN";
@@ -191,6 +208,10 @@
             case GESTURE_3_FINGER_SWIPE_LEFT: return "GESTURE_3_FINGER_SWIPE_LEFT";
             case GESTURE_3_FINGER_SWIPE_RIGHT: return "GESTURE_3_FINGER_SWIPE_RIGHT";
             case GESTURE_3_FINGER_SWIPE_UP: return "GESTURE_3_FINGER_SWIPE_UP";
+            case GESTURE_4_FINGER_SWIPE_DOWN: return "GESTURE_4_FINGER_SWIPE_DOWN";
+            case GESTURE_4_FINGER_SWIPE_LEFT: return "GESTURE_4_FINGER_SWIPE_LEFT";
+            case GESTURE_4_FINGER_SWIPE_RIGHT: return "GESTURE_4_FINGER_SWIPE_RIGHT";
+            case GESTURE_4_FINGER_SWIPE_UP: return "GESTURE_4_FINGER_SWIPE_UP";
             default: return Integer.toHexString(eventType);
         }
     }
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 2165fb3..11f7f46 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -388,6 +388,27 @@
      */
     public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32;
 
+    /** The user has performed a four-finger swipe up gesture on the touch screen. */
+    public static final int GESTURE_4_FINGER_SWIPE_UP = 33;
+
+    /** The user has performed a four-finger swipe down gesture on the touch screen. */
+    public static final int GESTURE_4_FINGER_SWIPE_DOWN = 34;
+
+    /** The user has performed a four-finger swipe left gesture on the touch screen. */
+    public static final int GESTURE_4_FINGER_SWIPE_LEFT = 35;
+
+    /** The user has performed a four-finger swipe right gesture on the touch screen. */
+    public static final int GESTURE_4_FINGER_SWIPE_RIGHT = 36;
+
+    /** The user has performed a four-finger single tap gesture on the touch screen. */
+    public static final int GESTURE_4_FINGER_SINGLE_TAP = 37;
+
+    /** The user has performed a four-finger double tap gesture on the touch screen. */
+    public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38;
+
+    /** The user has performed a four-finger triple tap gesture on the touch screen. */
+    public static final int GESTURE_4_FINGER_TRIPLE_TAP = 39;
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java
index 3b79d21..a821dad 100644
--- a/core/java/android/accessibilityservice/GestureDescription.java
+++ b/core/java/android/accessibilityservice/GestureDescription.java
@@ -40,7 +40,7 @@
  */
 public final class GestureDescription {
     /** Gestures may contain no more than this many strokes */
-    private static final int MAX_STROKE_COUNT = 10;
+    private static final int MAX_STROKE_COUNT = 20;
 
     /**
      * Upper bound on total gesture duration. Nearly all gestures will be much shorter.
@@ -194,7 +194,10 @@
         public Builder addStroke(@NonNull StrokeDescription strokeDescription) {
             if (mStrokes.size() >= MAX_STROKE_COUNT) {
                 throw new IllegalStateException(
-                        "Attempting to add too many strokes to a gesture");
+                        "Attempting to add too many strokes to a gesture. Maximum is "
+                                + MAX_STROKE_COUNT
+                                + ", got "
+                                + mStrokes.size());
             }
 
             mStrokes.add(strokeDescription);
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 5d170d3..b74be7e 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -30,6 +30,13 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_UP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_TRIPLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN;
@@ -133,6 +140,13 @@
                 new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this));
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
+        // Four-finger taps.
+        mMultiFingerGestures.add(
+                new MultiFingerMultiTap(mContext, 4, 1, GESTURE_4_FINGER_SINGLE_TAP, this));
+        mMultiFingerGestures.add(
+                new MultiFingerMultiTap(mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP, this));
+        mMultiFingerGestures.add(
+                new MultiFingerMultiTap(mContext, 4, 3, GESTURE_4_FINGER_TRIPLE_TAP, this));
         // Two-finger swipes.
         mMultiFingerGestures.add(
                 new MultiFingerSwipe(context, 2, DOWN, GESTURE_2_FINGER_SWIPE_DOWN, this));
@@ -151,6 +165,15 @@
                 new MultiFingerSwipe(context, 3, RIGHT, GESTURE_3_FINGER_SWIPE_RIGHT, this));
         mMultiFingerGestures.add(
                 new MultiFingerSwipe(context, 3, UP, GESTURE_3_FINGER_SWIPE_UP, this));
+        // Four-finger swipes.
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 4, DOWN, GESTURE_4_FINGER_SWIPE_DOWN, this));
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 4, LEFT, GESTURE_4_FINGER_SWIPE_LEFT, this));
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 4, RIGHT, GESTURE_4_FINGER_SWIPE_RIGHT, this));
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 4, UP, GESTURE_4_FINGER_SWIPE_UP, this));
     }
 
     /**
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
index 8249239..a14584a 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
@@ -139,7 +139,7 @@
         final int actionIndex = getActionIndex(rawEvent);
         final int pointerId = rawEvent.getPointerId(actionIndex);
         int pointerIndex = rawEvent.getPointerCount() - 1;
-        if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+        if (pointerId < 0) {
             // Nonsensical pointer id.
             cancelGesture(event, rawEvent, policyFlags);
             return;
@@ -185,7 +185,7 @@
         }
         final int actionIndex = getActionIndex(rawEvent);
         final int pointerId = rawEvent.getPointerId(actionIndex);
-        if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+        if (pointerId < 0) {
             // Nonsensical pointer id.
             cancelGesture(event, rawEvent, policyFlags);
             return;
@@ -224,7 +224,7 @@
         mCurrentFingerCount -= 1;
         final int actionIndex = getActionIndex(event);
         final int pointerId = event.getPointerId(actionIndex);
-        if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+        if (pointerId < 0) {
             // Nonsensical pointer id.
             cancelGesture(event, rawEvent, policyFlags);
             return;
@@ -250,11 +250,29 @@
 
     @Override
     protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        for (int pointerIndex = 0; pointerIndex < rawEvent.getPointerCount(); ++pointerIndex) {
+        for (int pointerIndex = 0; pointerIndex < mTargetFingerCount; ++pointerIndex) {
+            if (mPointerIds[pointerIndex] == INVALID_POINTER_ID) {
+                // Fingers have started to move before the required number of fingers are down.
+                // However, they can still move less than the touch slop and still be considered
+                // touching, not moving.
+                // So we just ignore fingers that haven't been assigned a pointer id and process
+                // those who have.
+                continue;
+            }
             if (DEBUG) {
                 Slog.d(getGestureName(), "Processing move on finger " + pointerIndex);
             }
             int index = rawEvent.findPointerIndex(mPointerIds[pointerIndex]);
+            if (index < 0) {
+                // This finger is not present in this event. It could have gone up just before this
+                // movement.
+                if (DEBUG) {
+                    Slog.d(
+                            getGestureName(),
+                            "Finger " + pointerIndex + " not found in this event. skipping.");
+                }
+                continue;
+            }
             final float x = rawEvent.getX(index);
             final float y = rawEvent.getY(index);
             if (x < 0f || y < 0f) {