Generate ACTION_CANCEL on joystick disconnect.

Bug: 11480300
Change-Id: I46706838eec0711c6bf345301e7b4ccbad83b063
diff --git a/include/input/Input.h b/include/input/Input.h
index ea9c4c2..bb5ceaf 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -22,11 +22,12 @@
  */
 
 #include <android/input.h>
-#include <utils/Vector.h>
+#include <utils/BitSet.h>
 #include <utils/KeyedVector.h>
-#include <utils/Timers.h>
 #include <utils/RefBase.h>
 #include <utils/String8.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
 
 /*
  * Additional private constants not defined in ndk/ui/input.h.
@@ -205,7 +206,11 @@
     float values[MAX_AXES];
 
     inline void clear() {
-        bits = 0;
+        BitSet64::clear(bits);
+    }
+
+    bool isEmpty() const {
+        return BitSet64::isEmpty(bits);
     }
 
     float getAxisValue(int32_t axis) const;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index ccbf52b..d9f22e9 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -158,16 +158,10 @@
 // --- PointerCoords ---
 
 float PointerCoords::getAxisValue(int32_t axis) const {
-    if (axis < 0 || axis > 63) {
+    if (axis < 0 || axis > 63 || !BitSet64::hasBit(bits, axis)){
         return 0;
     }
-
-    uint64_t axisBit = 1LL << axis;
-    if (!(bits & axisBit)) {
-        return 0;
-    }
-    uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
-    return values[index];
+    return values[BitSet64::getIndexOfBit(bits, axis)];
 }
 
 status_t PointerCoords::setAxisValue(int32_t axis, float value) {
@@ -175,22 +169,23 @@
         return NAME_NOT_FOUND;
     }
 
-    uint64_t axisBit = 1LL << axis;
-    uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
-    if (!(bits & axisBit)) {
+    uint32_t index = BitSet64::getIndexOfBit(bits, axis);
+    if (!BitSet64::hasBit(bits, axis)) {
         if (value == 0) {
             return OK; // axes with value 0 do not need to be stored
         }
-        uint32_t count = __builtin_popcountll(bits);
+
+        uint32_t count = BitSet64::count(bits);
         if (count >= MAX_AXES) {
             tooManyAxes(axis);
             return NO_MEMORY;
         }
-        bits |= axisBit;
+        BitSet64::markBit(bits, axis);
         for (uint32_t i = count; i > index; i--) {
             values[i] = values[i - 1];
         }
     }
+
     values[index] = value;
     return OK;
 }
@@ -222,7 +217,7 @@
 status_t PointerCoords::readFromParcel(Parcel* parcel) {
     bits = parcel->readInt64();
 
-    uint32_t count = __builtin_popcountll(bits);
+    uint32_t count = BitSet64::count(bits);
     if (count > MAX_AXES) {
         return BAD_VALUE;
     }
@@ -236,7 +231,7 @@
 status_t PointerCoords::writeToParcel(Parcel* parcel) const {
     parcel->writeInt64(bits);
 
-    uint32_t count = __builtin_popcountll(bits);
+    uint32_t count = BitSet64::count(bits);
     for (uint32_t i = 0; i < count; i++) {
         parcel->writeFloat(values[i]);
     }
@@ -253,7 +248,7 @@
     if (bits != other.bits) {
         return false;
     }
-    uint32_t count = __builtin_popcountll(bits);
+    uint32_t count = BitSet64::count(bits);
     for (uint32_t i = 0; i < count; i++) {
         if (values[i] != other.values[i]) {
             return false;
@@ -264,7 +259,7 @@
 
 void PointerCoords::copyFrom(const PointerCoords& other) {
     bits = other.bits;
-    uint32_t count = __builtin_popcountll(bits);
+    uint32_t count = BitSet64::count(bits);
     for (uint32_t i = 0; i < count; i++) {
         values[i] = other.values[i];
     }
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index f933681..56044ee 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -4108,18 +4108,39 @@
     case AMOTION_EVENT_ACTION_POINTER_UP:
     case AMOTION_EVENT_ACTION_POINTER_DOWN:
     case AMOTION_EVENT_ACTION_MOVE: {
+        if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) {
+            // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need to
+            // generate cancellation events for these since they're based in relative rather than
+            // absolute units.
+            return true;
+        }
+
         ssize_t index = findMotionMemento(entry, false /*hovering*/);
+
+        if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) {
+            // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all
+            // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral. Any
+            // other value and we need to track the motion so we can send cancellation events for
+            // anything generating fallback events (e.g. DPad keys for joystick movements).
+            if (index >= 0) {
+                if (entry->pointerCoords[0].isEmpty()) {
+                    mMotionMementos.removeAt(index);
+                } else {
+                    MotionMemento& memento = mMotionMementos.editItemAt(index);
+                    memento.setPointers(entry);
+                }
+            } else if (!entry->pointerCoords[0].isEmpty()) {
+                addMotionMemento(entry, flags, false /*hovering*/);
+            }
+
+            // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
+            return true;
+        }
         if (index >= 0) {
             MotionMemento& memento = mMotionMementos.editItemAt(index);
             memento.setPointers(entry);
             return true;
         }
-        if (actionMasked == AMOTION_EVENT_ACTION_MOVE
-                && (entry->source & (AINPUT_SOURCE_CLASS_JOYSTICK
-                        | AINPUT_SOURCE_CLASS_NAVIGATION))) {
-            // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
-            return true;
-        }
 #if DEBUG_OUTBOUND_EVENT_DETAILS
         ALOGD("Dropping inconsistent motion pointer up/down or move event: "
                 "deviceId=%d, source=%08x, actionMasked=%d",