Allow touches to slide out of the navigation bar.

Change-Id: I73cabba3d62f47829bf6217700ace56a27c42b1d
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index eff65c2..28df3fb 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -1412,6 +1412,50 @@
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
         }
+
+        // Check whether touches should slip outside of the current foreground window.
+        if (maskedAction == AMOTION_EVENT_ACTION_MOVE
+                && entry->pointerCount == 1
+                && mTempTouchState.isSlippery()) {
+            const MotionSample* sample = &entry->firstSample;
+            int32_t x = int32_t(sample->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+            int32_t y = int32_t(sample->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+
+            const InputWindow* oldTouchedWindow = mTempTouchState.getFirstForegroundWindow();
+            const InputWindow* newTouchedWindow = findTouchedWindowAtLocked(x, y);
+            if (oldTouchedWindow != newTouchedWindow && newTouchedWindow) {
+#if DEBUG_FOCUS
+                LOGD("Touch is slipping out of window %s into window %s.",
+                        oldTouchedWindow->name.string(), newTouchedWindow->name.string());
+#endif
+                // Make a slippery exit from the old window.
+                mTempTouchState.addOrUpdateWindow(oldTouchedWindow,
+                        InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0));
+
+                // Make a slippery entrance into the new window.
+                if (newTouchedWindow->supportsSplitTouch()) {
+                    isSplit = true;
+                }
+
+                int32_t targetFlags = InputTarget::FLAG_FOREGROUND
+                        | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
+                if (isSplit) {
+                    targetFlags |= InputTarget::FLAG_SPLIT;
+                }
+                if (isWindowObscuredAtPointLocked(newTouchedWindow, x, y)) {
+                    targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+                }
+
+                BitSet32 pointerIds;
+                if (isSplit) {
+                    pointerIds.markBit(entry->pointerProperties[0].id);
+                }
+                mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds);
+
+                // Split the batch here so we send exactly one sample.
+                *outSplitBatchAfterSample = &entry->firstSample;
+            }
+        }
     }
 
     if (newHoverWindow != mLastHoverWindow) {
@@ -1884,6 +1928,10 @@
             resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
             resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_IS);
+    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+            resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
+    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+            resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
 
     // If the outbound queue was previously empty, start the dispatch cycle going.
     if (wasEmpty && !connection->outboundQueue.isEmpty()) {
@@ -1985,6 +2033,10 @@
             action = AMOTION_EVENT_ACTION_HOVER_EXIT;
         } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
             action = AMOTION_EVENT_ACTION_HOVER_ENTER;
+        } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
+            action = AMOTION_EVENT_ACTION_CANCEL;
+        } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
+            action = AMOTION_EVENT_ACTION_DOWN;
         }
         if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
             flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
@@ -4386,6 +4438,9 @@
         TouchedWindow& touchedWindow = windows.editItemAt(i);
         if (touchedWindow.window == window) {
             touchedWindow.targetFlags |= targetFlags;
+            if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
+                touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
+            }
             touchedWindow.pointerIds.value |= pointerIds.value;
             return;
         }
@@ -4403,7 +4458,8 @@
 void InputDispatcher::TouchState::filterNonAsIsTouchWindows() {
     for (size_t i = 0 ; i < windows.size(); ) {
         TouchedWindow& window = windows.editItemAt(i);
-        if (window.targetFlags & InputTarget::FLAG_DISPATCH_AS_IS) {
+        if (window.targetFlags & (InputTarget::FLAG_DISPATCH_AS_IS
+                | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {
             window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;
             window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;
             i += 1;
@@ -4413,15 +4469,32 @@
     }
 }
 
-const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() {
+const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() const {
     for (size_t i = 0; i < windows.size(); i++) {
-        if (windows[i].targetFlags & InputTarget::FLAG_FOREGROUND) {
-            return windows[i].window;
+        const TouchedWindow& window = windows.itemAt(i);
+        if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
+            return window.window;
         }
     }
     return NULL;
 }
 
+bool InputDispatcher::TouchState::isSlippery() const {
+    // Must have exactly one foreground window.
+    bool haveSlipperyForegroundWindow = false;
+    for (size_t i = 0; i < windows.size(); i++) {
+        const TouchedWindow& window = windows.itemAt(i);
+        if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
+            if (haveSlipperyForegroundWindow
+                    || !(window.window->layoutParamsFlags & InputWindow::FLAG_SLIPPERY)) {
+                return false;
+            }
+            haveSlipperyForegroundWindow = true;
+        }
+    }
+    return haveSlipperyForegroundWindow;
+}
+
 
 // --- InputDispatcherThread ---
 
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 39d4aeb..676d162 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -121,11 +121,23 @@
          * The event is transmuted into ACTION_HOVER_ENTER. */
         FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11,
 
+        /* This flag indicates that the event should be canceled.
+         * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips
+         * outside of a window. */
+        FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12,
+
+        /* This flag indicates that the event should be dispatched as an initial down.
+         * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips
+         * into a new window. */
+        FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13,
+
         /* Mask for all dispatch modes. */
         FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS
                 | FLAG_DISPATCH_AS_OUTSIDE
                 | FLAG_DISPATCH_AS_HOVER_ENTER
-                | FLAG_DISPATCH_AS_HOVER_EXIT,
+                | FLAG_DISPATCH_AS_HOVER_EXIT
+                | FLAG_DISPATCH_AS_SLIPPERY_EXIT
+                | FLAG_DISPATCH_AS_SLIPPERY_ENTER,
     };
 
     // The input channel to be targeted.
@@ -950,9 +962,10 @@
         ~TouchState();
         void reset();
         void copyFrom(const TouchState& other);
-        void addOrUpdateWindow(const InputWindow* window, int32_t targetFlags, BitSet32 pointerIds);
+        void addOrUpdateWindow(const InputWindow* window,int32_t targetFlags, BitSet32 pointerIds);
         void filterNonAsIsTouchWindows();
-        const InputWindow* getFirstForegroundWindow();
+        const InputWindow* getFirstForegroundWindow() const;
+        bool isSlippery() const;
     };
 
     TouchState mTouchState;
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index d7d819d..93c9b5f 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -80,6 +80,10 @@
         FLAG_TURN_SCREEN_ON = 0x00200000,
         FLAG_DISMISS_KEYGUARD = 0x00400000,
         FLAG_SPLIT_TOUCH = 0x00800000,
+        FLAG_HARDWARE_ACCELERATED = 0x01000000,
+        FLAG_HARDWARE_ACCELERATED_SYSTEM = 0x02000000,
+        FLAG_SLIPPERY = 0x04000000,
+        FLAG_NEEDS_MENU_KEY = 0x08000000,
         FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
         FLAG_COMPATIBLE_WINDOW = 0x20000000,
         FLAG_SYSTEM_ERROR = 0x40000000,