Fuse stylus and touch screen data.

TODO: Ensure we dispatch new pressure and button data when stylus
is stationary.

Change-Id: I10a665a9b81ff4b9acd01bf72f0fc2e0c2abc203
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index b39f367..5654799 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -39,12 +39,16 @@
 // Log debug messages about the vibrator.
 #define DEBUG_VIBRATOR 0
 
+// Log debug messages about fusing stylus data.
+#define DEBUG_STYLUS_FUSION 0
+
 #include "InputReader.h"
 
 #include <cutils/log.h>
 #include <input/Keyboard.h>
 #include <input/VirtualKeyMap.h>
 
+#include <inttypes.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -65,6 +69,10 @@
 // Maximum number of slots supported when using the slot-based Multitouch Protocol B.
 static const size_t MAX_SLOTS = 32;
 
+// Maximum amount of latency to add to touch events while waiting for data from an
+// external stylus.
+static const nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(32);
+
 // --- Static Functions ---
 
 template<typename T>
@@ -381,6 +389,10 @@
 
     mDevices.add(deviceId, device);
     bumpGenerationLocked();
+
+    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+        notifyExternalStylusPresenceChanged();
+    }
 }
 
 void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
@@ -403,6 +415,10 @@
                 device->getId(), device->getName().string(), device->getSources());
     }
 
+    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+        notifyExternalStylusPresenceChanged();
+    }
+
     device->reset(when);
     delete device;
 }
@@ -469,6 +485,11 @@
         device->addMapper(new JoystickInputMapper(device));
     }
 
+    // External stylus-like devices.
+    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+        device->addMapper(new ExternalStylusInputMapper(device));
+    }
+
     return device;
 }
 
@@ -539,6 +560,27 @@
     return mGlobalMetaState;
 }
 
+void InputReader::notifyExternalStylusPresenceChanged() {
+    refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
+}
+
+void InputReader::getExternalStylusDevicesLocked(Vector<InputDeviceInfo>& outDevices) {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
+            outDevices.push();
+            device->getDeviceInfo(&outDevices.editTop());
+        }
+    }
+}
+
+void InputReader::dispatchExternalStylusState(const StylusState& state) {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        device->updateExternalStylusState(state);
+    }
+}
+
 void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) {
     mDisableVirtualKeysTimeout = time;
 }
@@ -829,6 +871,15 @@
     return mReader->bumpGenerationLocked();
 }
 
+void InputReader::ContextImpl::getExternalStylusDevices(Vector<InputDeviceInfo>& outDevices) {
+    // lock is already held by whatever called refreshConfigurationLocked
+    mReader->getExternalStylusDevicesLocked(outDevices);
+}
+
+void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
+    mReader->dispatchExternalStylusState(state);
+}
+
 InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
     return mReader->mPolicy.get();
 }
@@ -1012,6 +1063,14 @@
     }
 }
 
+void InputDevice::updateExternalStylusState(const StylusState& state) {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->updateExternalStylusState(state);
+    }
+}
+
 void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
     outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias,
             mIsExternal, mHasMic);
@@ -1290,7 +1349,9 @@
 void TouchButtonAccumulator::reset(InputDevice* device) {
     mBtnTouch = device->isKeyPressed(BTN_TOUCH);
     mBtnStylus = device->isKeyPressed(BTN_STYLUS);
-    mBtnStylus2 = device->isKeyPressed(BTN_STYLUS);
+    // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
+    mBtnStylus2 =
+            device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0);
     mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER);
     mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN);
     mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER);
@@ -1331,6 +1392,7 @@
             mBtnStylus = rawEvent->value;
             break;
         case BTN_STYLUS2:
+        case BTN_0:// BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
             mBtnStylus2 = rawEvent->value;
             break;
         case BTN_TOOL_FINGER:
@@ -1806,6 +1868,10 @@
     return 0;
 }
 
+void InputMapper::updateExternalStylusState(const StylusState& state) {
+
+}
+
 void InputMapper::fadePointer() {
 }
 
@@ -1827,6 +1893,12 @@
     }
 }
 
+void InputMapper::dumpStylusState(String8& dump, const StylusState& state) {
+    dump.appendFormat(INDENT4 "When: %" PRId64 "\n", state.when);
+    dump.appendFormat(INDENT4 "Pressure: %f\n", state.pressure);
+    dump.appendFormat(INDENT4 "Button State: 0x%08x\n", state.buttons);
+    dump.appendFormat(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType);
+}
 
 // --- SwitchInputMapper ---
 
@@ -2708,12 +2780,12 @@
     dump.appendFormat(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter);
     dump.appendFormat(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale);
 
-    dump.appendFormat(INDENT3 "Last Button State: 0x%08x\n", mLastButtonState);
+    dump.appendFormat(INDENT3 "Last Button State: 0x%08x\n", mLastRawState.buttonState);
 
     dump.appendFormat(INDENT3 "Last Raw Touch: pointerCount=%d\n",
-            mLastRawPointerData.pointerCount);
-    for (uint32_t i = 0; i < mLastRawPointerData.pointerCount; i++) {
-        const RawPointerData::Pointer& pointer = mLastRawPointerData.pointers[i];
+            mLastRawState.rawPointerData.pointerCount);
+    for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) {
+        const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i];
         dump.appendFormat(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
                 "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
                 "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, "
@@ -2726,10 +2798,11 @@
     }
 
     dump.appendFormat(INDENT3 "Last Cooked Touch: pointerCount=%d\n",
-            mLastCookedPointerData.pointerCount);
-    for (uint32_t i = 0; i < mLastCookedPointerData.pointerCount; i++) {
-        const PointerProperties& pointerProperties = mLastCookedPointerData.pointerProperties[i];
-        const PointerCoords& pointerCoords = mLastCookedPointerData.pointerCoords[i];
+            mLastCookedState.cookedPointerData.pointerCount);
+    for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) {
+        const PointerProperties& pointerProperties =
+                mLastCookedState.cookedPointerData.pointerProperties[i];
+        const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i];
         dump.appendFormat(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, "
                 "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, toolMinor=%0.3f, "
                 "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, "
@@ -2746,9 +2819,18 @@
                 pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT),
                 pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE),
                 pointerProperties.toolType,
-                toString(mLastCookedPointerData.isHovering(i)));
+                toString(mLastCookedState.cookedPointerData.isHovering(i)));
     }
 
+    dump.append(INDENT3 "Stylus Fusion:\n");
+    dump.appendFormat(INDENT4 "ExternalStylusConnected: %s\n",
+            toString(mExternalStylusConnected));
+    dump.appendFormat(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId);
+    dump.appendFormat(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
+            mExternalStylusDataTimeout);
+    dump.append(INDENT3 "External Stylus State:\n");
+    dumpStylusState(dump, mExternalStylusState);
+
     if (mDeviceMode == DEVICE_MODE_POINTER) {
         dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n");
         dump.appendFormat(INDENT4 "XMovementScale: %0.3f\n",
@@ -2786,7 +2868,7 @@
         resolveCalibration();
     }
 
-    if (!changes || (changes & InputReaderConfiguration::TOUCH_AFFINE_TRANSFORMATION)) {
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) {
         // Update location calibration to reflect current settings
         updateAffineTransformation();
     }
@@ -2801,7 +2883,8 @@
     bool resetNeeded = false;
     if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO
             | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT
-            | InputReaderConfiguration::CHANGE_SHOW_TOUCHES))) {
+            | InputReaderConfiguration::CHANGE_SHOW_TOUCHES
+            | InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
         // Configure device sources, surface dimensions, orientation and
         // scaling factors.
         configureSurface(when, &resetNeeded);
@@ -2814,6 +2897,16 @@
     }
 }
 
+void TouchInputMapper::resolveExternalStylusPresence() {
+    Vector<InputDeviceInfo> devices;
+    mContext->getExternalStylusDevices(devices);
+    mExternalStylusConnected = !devices.isEmpty();
+
+    if (!mExternalStylusConnected) {
+        resetExternalStylus();
+    }
+}
+
 void TouchInputMapper::configureParameters() {
     // Use the pointer presentation mode for devices that do not support distinct
     // multitouch.  The spot-based presentation relies on being able to accurately
@@ -2950,9 +3043,15 @@
     dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot");
 }
 
+bool TouchInputMapper::hasExternalStylus() const {
+    return mExternalStylusConnected;
+}
+
 void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
     int32_t oldDeviceMode = mDeviceMode;
 
+    resolveExternalStylusPresence();
+
     // Determine device mode.
     if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER
             && mConfig.pointerGesturesEnabled) {
@@ -2965,7 +3064,7 @@
             && mParameters.hasAssociatedDisplay) {
         mSource = AINPUT_SOURCE_TOUCHSCREEN;
         mDeviceMode = DEVICE_MODE_DIRECT;
-        if (hasStylus()) {
+        if (hasStylus() || hasExternalStylus()) {
             mSource |= AINPUT_SOURCE_STYLUS;
         }
     } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
@@ -3710,28 +3809,21 @@
     mWheelXVelocityControl.reset();
     mWheelYVelocityControl.reset();
 
-    mCurrentRawPointerData.clear();
-    mLastRawPointerData.clear();
-    mCurrentCookedPointerData.clear();
-    mLastCookedPointerData.clear();
-    mCurrentButtonState = 0;
-    mLastButtonState = 0;
-    mCurrentRawVScroll = 0;
-    mCurrentRawHScroll = 0;
-    mCurrentFingerIdBits.clear();
-    mLastFingerIdBits.clear();
-    mCurrentStylusIdBits.clear();
-    mLastStylusIdBits.clear();
-    mCurrentMouseIdBits.clear();
-    mLastMouseIdBits.clear();
+    mRawStatesPending.clear();
+    mCurrentRawState.clear();
+    mCurrentCookedState.clear();
+    mLastRawState.clear();
+    mLastCookedState.clear();
     mPointerUsage = POINTER_USAGE_NONE;
     mSentHoverEnter = false;
+    mHavePointerIds = false;
     mDownTime = 0;
 
     mCurrentVirtualKey.down = false;
 
     mPointerGesture.reset();
     mPointerSimple.reset();
+    resetExternalStylus();
 
     if (mPointerController != NULL) {
         mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
@@ -3741,6 +3833,13 @@
     InputMapper::reset(when);
 }
 
+void TouchInputMapper::resetExternalStylus() {
+    mExternalStylusState.clear();
+    mExternalStylusId = -1;
+    mExternalStylusDataTimeout = LLONG_MAX;
+    mExternalStylusDataPending = false;
+}
+
 void TouchInputMapper::process(const RawEvent* rawEvent) {
     mCursorButtonAccumulator.process(rawEvent);
     mCursorScrollAccumulator.process(rawEvent);
@@ -3752,155 +3851,264 @@
 }
 
 void TouchInputMapper::sync(nsecs_t when) {
+    const RawState* last = mRawStatesPending.isEmpty() ?
+            &mCurrentRawState : &mRawStatesPending.top();
+
+    // Push a new state.
+    mRawStatesPending.push();
+    RawState* next = &mRawStatesPending.editTop();
+    next->clear();
+    next->when = when;
+
     // Sync button state.
-    mCurrentButtonState = mTouchButtonAccumulator.getButtonState()
+    next->buttonState = mTouchButtonAccumulator.getButtonState()
             | mCursorButtonAccumulator.getButtonState();
 
-    // Sync scroll state.
-    mCurrentRawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
-    mCurrentRawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
+    // Sync scroll
+    next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
+    next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
     mCursorScrollAccumulator.finishSync();
 
-    // Sync touch state.
-    bool havePointerIds = true;
-    mCurrentRawPointerData.clear();
-    syncTouch(when, &havePointerIds);
+    // Sync touch
+    syncTouch(when, next);
+
+    // Assign pointer ids.
+    if (!mHavePointerIds) {
+        assignPointerIds(last, next);
+    }
 
 #if DEBUG_RAW_EVENTS
-    if (!havePointerIds) {
-        ALOGD("syncTouch: pointerCount %d -> %d, no pointer ids",
-                mLastRawPointerData.pointerCount,
-                mCurrentRawPointerData.pointerCount);
-    } else {
-        ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
-                "hovering ids 0x%08x -> 0x%08x",
-                mLastRawPointerData.pointerCount,
-                mCurrentRawPointerData.pointerCount,
-                mLastRawPointerData.touchingIdBits.value,
-                mCurrentRawPointerData.touchingIdBits.value,
-                mLastRawPointerData.hoveringIdBits.value,
-                mCurrentRawPointerData.hoveringIdBits.value);
-    }
+    ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
+            "hovering ids 0x%08x -> 0x%08x",
+            last->rawPointerData.pointerCount,
+            next->rawPointerData.pointerCount,
+            last->rawPointerData.touchingIdBits.value,
+            next->rawPointerData.touchingIdBits.value,
+            last->rawPointerData.hoveringIdBits.value,
+            next->rawPointerData.hoveringIdBits.value);
 #endif
 
-    // Reset state that we will compute below.
-    mCurrentFingerIdBits.clear();
-    mCurrentStylusIdBits.clear();
-    mCurrentMouseIdBits.clear();
-    mCurrentCookedPointerData.clear();
+    processRawTouches(false /*timeout*/);
+}
 
+void TouchInputMapper::processRawTouches(bool timeout) {
     if (mDeviceMode == DEVICE_MODE_DISABLED) {
         // Drop all input if the device is disabled.
-        mCurrentRawPointerData.clear();
-        mCurrentButtonState = 0;
-    } else {
-        // Preprocess pointer data.
-        if (!havePointerIds) {
-            assignPointerIds();
-        }
-
-        // Handle policy on initial down or hover events.
-        uint32_t policyFlags = 0;
-        bool initialDown = mLastRawPointerData.pointerCount == 0
-                && mCurrentRawPointerData.pointerCount != 0;
-        bool buttonsPressed = mCurrentButtonState & ~mLastButtonState;
-        if (initialDown || buttonsPressed) {
-            // If this is a touch screen, hide the pointer on an initial down.
-            if (mDeviceMode == DEVICE_MODE_DIRECT) {
-                getContext()->fadePointer();
-            }
-
-            if (mParameters.wake) {
-                policyFlags |= POLICY_FLAG_WAKE;
-            }
-        }
-
-        // Synthesize key down from raw buttons if needed.
-        synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
-                policyFlags, mLastButtonState, mCurrentButtonState);
-
-        // Consume raw off-screen touches before cooking pointer data.
-        // If touches are consumed, subsequent code will not receive any pointer data.
-        if (consumeRawTouches(when, policyFlags)) {
-            mCurrentRawPointerData.clear();
-        }
-
-        // Cook pointer data.  This call populates the mCurrentCookedPointerData structure
-        // with cooked pointer data that has the same ids and indices as the raw data.
-        // The following code can use either the raw or cooked data, as needed.
-        cookPointerData();
-
-        // Dispatch the touches either directly or by translation through a pointer on screen.
-        if (mDeviceMode == DEVICE_MODE_POINTER) {
-            for (BitSet32 idBits(mCurrentRawPointerData.touchingIdBits); !idBits.isEmpty(); ) {
-                uint32_t id = idBits.clearFirstMarkedBit();
-                const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
-                if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
-                        || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
-                    mCurrentStylusIdBits.markBit(id);
-                } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER
-                        || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
-                    mCurrentFingerIdBits.markBit(id);
-                } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {
-                    mCurrentMouseIdBits.markBit(id);
-                }
-            }
-            for (BitSet32 idBits(mCurrentRawPointerData.hoveringIdBits); !idBits.isEmpty(); ) {
-                uint32_t id = idBits.clearFirstMarkedBit();
-                const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
-                if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
-                        || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
-                    mCurrentStylusIdBits.markBit(id);
-                }
-            }
-
-            // Stylus takes precedence over all tools, then mouse, then finger.
-            PointerUsage pointerUsage = mPointerUsage;
-            if (!mCurrentStylusIdBits.isEmpty()) {
-                mCurrentMouseIdBits.clear();
-                mCurrentFingerIdBits.clear();
-                pointerUsage = POINTER_USAGE_STYLUS;
-            } else if (!mCurrentMouseIdBits.isEmpty()) {
-                mCurrentFingerIdBits.clear();
-                pointerUsage = POINTER_USAGE_MOUSE;
-            } else if (!mCurrentFingerIdBits.isEmpty() || isPointerDown(mCurrentButtonState)) {
-                pointerUsage = POINTER_USAGE_GESTURES;
-            }
-
-            dispatchPointerUsage(when, policyFlags, pointerUsage);
-        } else {
-            if (mDeviceMode == DEVICE_MODE_DIRECT
-                    && mConfig.showTouches && mPointerController != NULL) {
-                mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
-                mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-
-                mPointerController->setButtonState(mCurrentButtonState);
-                mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords,
-                        mCurrentCookedPointerData.idToIndex,
-                        mCurrentCookedPointerData.touchingIdBits);
-            }
-
-            dispatchHoverExit(when, policyFlags);
-            dispatchTouches(when, policyFlags);
-            dispatchHoverEnterAndMove(when, policyFlags);
-        }
-
-        // Synthesize key up from raw buttons if needed.
-        synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
-                policyFlags, mLastButtonState, mCurrentButtonState);
+        mCurrentRawState.clear();
+        mRawStatesPending.clear();
+        return;
     }
 
-    // Copy current touch to last touch in preparation for the next cycle.
-    mLastRawPointerData.copyFrom(mCurrentRawPointerData);
-    mLastCookedPointerData.copyFrom(mCurrentCookedPointerData);
-    mLastButtonState = mCurrentButtonState;
-    mLastFingerIdBits = mCurrentFingerIdBits;
-    mLastStylusIdBits = mCurrentStylusIdBits;
-    mLastMouseIdBits = mCurrentMouseIdBits;
+    // Drain any pending touch states. The invariant here is that the mCurrentRawState is always
+    // valid and must go through the full cook and dispatch cycle. This ensures that anything
+    // touching the current state will only observe the events that have been dispatched to the
+    // rest of the pipeline.
+    const size_t N = mRawStatesPending.size();
+    size_t count;
+    for(count = 0; count < N; count++) {
+        const RawState& next = mRawStatesPending[count];
+
+        // A failure to assign the stylus id means that we're waiting on stylus data
+        // and so should defer the rest of the pipeline.
+        if (assignExternalStylusId(next, timeout)) {
+            break;
+        }
+
+        // All ready to go.
+        mExternalStylusDataPending = false;
+        mCurrentRawState.copyFrom(next);
+        cookAndDispatch(mCurrentRawState.when);
+    }
+    if (count != 0) {
+        mRawStatesPending.removeItemsAt(0, count);
+    }
+
+    // TODO: If we still have pending stylus data that hasn't been mapped to a touch yet
+    // then set a timeout to synthesize a new touch event with the new data.  Otherwise
+    // we will lose button and pressure information while the touch is stationary.
+    if (mExternalStylusDataPending) {
+        ALOGD("STYLUS TODO: Wiggle wiggle wiggle.");
+    }
+}
+
+void TouchInputMapper::cookAndDispatch(nsecs_t when) {
+    // Always start with a clean state.
+    mCurrentCookedState.clear();
+
+    // Apply stylus buttons to current raw state.
+    applyExternalStylusButtonState(when);
+
+    // Handle policy on initial down or hover events.
+    bool initialDown = mLastRawState.rawPointerData.pointerCount == 0
+            && mCurrentRawState.rawPointerData.pointerCount != 0;
+
+    uint32_t policyFlags = 0;
+    bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
+    if (initialDown || buttonsPressed) {
+        // If this is a touch screen, hide the pointer on an initial down.
+        if (mDeviceMode == DEVICE_MODE_DIRECT) {
+            getContext()->fadePointer();
+        }
+
+        if (mParameters.wake) {
+            policyFlags |= POLICY_FLAG_WAKE;
+        }
+    }
+
+    // Consume raw off-screen touches before cooking pointer data.
+    // If touches are consumed, subsequent code will not receive any pointer data.
+    if (consumeRawTouches(when, policyFlags)) {
+        mCurrentRawState.rawPointerData.clear();
+    }
+
+    // Cook pointer data.  This call populates the mCurrentCookedState.cookedPointerData structure
+    // with cooked pointer data that has the same ids and indices as the raw data.
+    // The following code can use either the raw or cooked data, as needed.
+    cookPointerData();
+
+    // Apply stylus pressure to current cooked state.
+    applyExternalStylusTouchState(when);
+
+    // Synthesize key down from raw buttons if needed.
+    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
+            policyFlags, mLastRawState.buttonState, mCurrentRawState.buttonState);
+
+    // Dispatch the touches either directly or by translation through a pointer on screen.
+    if (mDeviceMode == DEVICE_MODE_POINTER) {
+        for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits);
+                !idBits.isEmpty(); ) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            const RawPointerData::Pointer& pointer =
+                    mCurrentRawState.rawPointerData.pointerForId(id);
+            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
+                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+                mCurrentCookedState.stylusIdBits.markBit(id);
+            } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER
+                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+                mCurrentCookedState.fingerIdBits.markBit(id);
+            } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {
+                mCurrentCookedState.mouseIdBits.markBit(id);
+            }
+        }
+        for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits);
+                !idBits.isEmpty(); ) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            const RawPointerData::Pointer& pointer =
+                    mCurrentRawState.rawPointerData.pointerForId(id);
+            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
+                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+                mCurrentCookedState.stylusIdBits.markBit(id);
+            }
+        }
+
+        // Stylus takes precedence over all tools, then mouse, then finger.
+        PointerUsage pointerUsage = mPointerUsage;
+        if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
+            mCurrentCookedState.mouseIdBits.clear();
+            mCurrentCookedState.fingerIdBits.clear();
+            pointerUsage = POINTER_USAGE_STYLUS;
+        } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
+            mCurrentCookedState.fingerIdBits.clear();
+            pointerUsage = POINTER_USAGE_MOUSE;
+        } else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
+                isPointerDown(mCurrentRawState.buttonState)) {
+            pointerUsage = POINTER_USAGE_GESTURES;
+        }
+
+        dispatchPointerUsage(when, policyFlags, pointerUsage);
+    } else {
+        if (mDeviceMode == DEVICE_MODE_DIRECT
+                && mConfig.showTouches && mPointerController != NULL) {
+            mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
+            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+
+            mPointerController->setButtonState(mCurrentRawState.buttonState);
+            mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
+                    mCurrentCookedState.cookedPointerData.idToIndex,
+                    mCurrentCookedState.cookedPointerData.touchingIdBits);
+        }
+
+        dispatchHoverExit(when, policyFlags);
+        dispatchTouches(when, policyFlags);
+        dispatchHoverEnterAndMove(when, policyFlags);
+    }
+
+    // Synthesize key up from raw buttons if needed.
+    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
+            policyFlags, mLastRawState.buttonState, mCurrentRawState.buttonState);
 
     // Clear some transient state.
-    mCurrentRawVScroll = 0;
-    mCurrentRawHScroll = 0;
+    mCurrentRawState.rawVScroll = 0;
+    mCurrentRawState.rawHScroll = 0;
+
+    // Copy current touch to last touch in preparation for the next cycle.
+    mLastRawState.copyFrom(mCurrentRawState);
+    mLastCookedState.copyFrom(mCurrentCookedState);
+}
+
+void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
+    if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus()) {
+        mCurrentRawState.buttonState |= mExternalStylusState.buttons;
+    }
+}
+
+void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) {
+    CookedPointerData& cpd = mCurrentCookedState.cookedPointerData;
+    if (mExternalStylusId != -1 && cpd.isTouching(mExternalStylusId)) {
+        if (mExternalStylusState.pressure != 0.0f) {
+            PointerCoords& coords = cpd.editPointerCoordsWithId(mExternalStylusId);
+            coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, mExternalStylusState.pressure);
+        }
+
+        PointerProperties& properties = cpd.editPointerPropertiesWithId(mExternalStylusId);
+        if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+            properties.toolType = mExternalStylusState.toolType;
+        }
+    }
+}
+
+bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) {
+    if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) {
+        return false;
+    }
+
+    const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0
+            && state.rawPointerData.pointerCount != 0;
+    if (initialDown) {
+        if (mExternalStylusState.pressure != 0.0f) {
+#if DEBUG_STYLUS_FUSION
+            ALOGD("Have both stylus and touch data, beginning fusion");
+#endif
+            mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit();
+        } else if (timeout) {
+#if DEBUG_STYLUS_FUSION
+            ALOGD("Timeout expired, assuming touch is not a stylus.");
+#endif
+            resetExternalStylus();
+        } else {
+            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+            if (mExternalStylusDataTimeout == LLONG_MAX) {
+                mExternalStylusDataTimeout = now + EXTERNAL_STYLUS_DATA_TIMEOUT;
+            }
+#if DEBUG_STYLUS_FUSION
+            ALOGD("No stylus data but stylus is connected, requesting timeout "
+                    "(%" PRId64 "ms)", mExternalStylusDataTimeout);
+#endif
+            getContext()->requestTimeoutAtTime(mExternalStylusDataTimeout);
+            return true;
+        }
+    }
+
+    // Check if the stylus pointer has gone up.
+    if (mExternalStylusId != -1 &&
+            !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) {
+#if DEBUG_STYLUS_FUSION
+            ALOGD("Stylus pointer is going up");
+#endif
+        mExternalStylusId = -1;
+    }
+
+    return false;
 }
 
 void TouchInputMapper::timeoutExpired(nsecs_t when) {
@@ -3908,13 +4116,32 @@
         if (mPointerUsage == POINTER_USAGE_GESTURES) {
             dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
         }
+    } else if (mDeviceMode == DEVICE_MODE_DIRECT) {
+        if (mExternalStylusDataTimeout < when) {
+            mExternalStylusDataTimeout = LLONG_MAX;
+            processRawTouches(true /*timeout*/);
+        } else if (mExternalStylusDataTimeout != LLONG_MAX) {
+            getContext()->requestTimeoutAtTime(mExternalStylusDataTimeout);
+        }
+    }
+}
+
+void TouchInputMapper::updateExternalStylusState(const StylusState& state) {
+    if (mExternalStylusId != -1 || mExternalStylusDataTimeout != LLONG_MAX) {
+        // We're either in the middle of a fused stream of data or we're waiting on data before
+        // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus
+        // data.
+        mExternalStylusDataTimeout = LLONG_MAX;
+        mExternalStylusDataPending = true;
+        mExternalStylusState.copyFrom(state);
+        processRawTouches(false /*timeout*/);
     }
 }
 
 bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
     // Check for release of a virtual key.
     if (mCurrentVirtualKey.down) {
-        if (mCurrentRawPointerData.touchingIdBits.isEmpty()) {
+        if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
             // Pointer went up while virtual key was down.
             mCurrentVirtualKey.down = false;
             if (!mCurrentVirtualKey.ignored) {
@@ -3929,9 +4156,10 @@
             return true;
         }
 
-        if (mCurrentRawPointerData.touchingIdBits.count() == 1) {
-            uint32_t id = mCurrentRawPointerData.touchingIdBits.firstMarkedBit();
-            const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
+        if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
+            uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
+            const RawPointerData::Pointer& pointer =
+                    mCurrentRawState.rawPointerData.pointerForId(id);
             const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
             if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
                 // Pointer is still within the space of the virtual key.
@@ -3956,15 +4184,15 @@
         }
     }
 
-    if (mLastRawPointerData.touchingIdBits.isEmpty()
-            && !mCurrentRawPointerData.touchingIdBits.isEmpty()) {
+    if (mLastRawState.rawPointerData.touchingIdBits.isEmpty()
+            && !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
         // Pointer just went down.  Check for virtual key press or off-screen touches.
-        uint32_t id = mCurrentRawPointerData.touchingIdBits.firstMarkedBit();
-        const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
+        uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
+        const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
         if (!isPointInsideSurface(pointer.x, pointer.y)) {
             // If exactly one pointer went down, check for virtual key hit.
             // Otherwise we will drop the entire stroke.
-            if (mCurrentRawPointerData.touchingIdBits.count() == 1) {
+            if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
                 const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
                 if (virtualKey) {
                     mCurrentVirtualKey.down = true;
@@ -4004,7 +4232,8 @@
     //    area and accidentally triggers a virtual key.  This often happens when virtual keys
     //    are layed out below the screen near to where the on screen keyboard's space bar
     //    is displayed.
-    if (mConfig.virtualKeyQuietTime > 0 && !mCurrentRawPointerData.touchingIdBits.isEmpty()) {
+    if (mConfig.virtualKeyQuietTime > 0 &&
+            !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
         mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
     }
     return false;
@@ -4024,10 +4253,10 @@
 }
 
 void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
-    BitSet32 currentIdBits = mCurrentCookedPointerData.touchingIdBits;
-    BitSet32 lastIdBits = mLastCookedPointerData.touchingIdBits;
+    BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
+    BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
     int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t buttonState = mCurrentButtonState;
+    int32_t buttonState = mCurrentRawState.buttonState;
 
     if (currentIdBits == lastIdBits) {
         if (!currentIdBits.isEmpty()) {
@@ -4036,9 +4265,9 @@
             dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState,
                     AMOTION_EVENT_EDGE_FLAG_NONE,
-                    mCurrentCookedPointerData.pointerProperties,
-                    mCurrentCookedPointerData.pointerCoords,
-                    mCurrentCookedPointerData.idToIndex,
+                    mCurrentCookedState.cookedPointerData.pointerProperties,
+                    mCurrentCookedState.cookedPointerData.pointerCoords,
+                    mCurrentCookedState.cookedPointerData.idToIndex,
                     currentIdBits, -1,
                     mOrientedXPrecision, mOrientedYPrecision, mDownTime);
         }
@@ -4053,14 +4282,14 @@
         // Update last coordinates of pointers that have moved so that we observe the new
         // pointer positions at the same time as other pointers that have just gone up.
         bool moveNeeded = updateMovedPointers(
-                mCurrentCookedPointerData.pointerProperties,
-                mCurrentCookedPointerData.pointerCoords,
-                mCurrentCookedPointerData.idToIndex,
-                mLastCookedPointerData.pointerProperties,
-                mLastCookedPointerData.pointerCoords,
-                mLastCookedPointerData.idToIndex,
+                mCurrentCookedState.cookedPointerData.pointerProperties,
+                mCurrentCookedState.cookedPointerData.pointerCoords,
+                mCurrentCookedState.cookedPointerData.idToIndex,
+                mLastCookedState.cookedPointerData.pointerProperties,
+                mLastCookedState.cookedPointerData.pointerCoords,
+                mLastCookedState.cookedPointerData.idToIndex,
                 moveIdBits);
-        if (buttonState != mLastButtonState) {
+        if (buttonState != mLastRawState.buttonState) {
             moveNeeded = true;
         }
 
@@ -4070,9 +4299,9 @@
 
             dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_POINTER_UP, 0, metaState, buttonState, 0,
-                    mLastCookedPointerData.pointerProperties,
-                    mLastCookedPointerData.pointerCoords,
-                    mLastCookedPointerData.idToIndex,
+                    mLastCookedState.cookedPointerData.pointerProperties,
+                    mLastCookedState.cookedPointerData.pointerCoords,
+                    mLastCookedState.cookedPointerData.idToIndex,
                     dispatchedIdBits, upId,
                     mOrientedXPrecision, mOrientedYPrecision, mDownTime);
             dispatchedIdBits.clearBit(upId);
@@ -4085,9 +4314,9 @@
             ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
             dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, 0,
-                    mCurrentCookedPointerData.pointerProperties,
-                    mCurrentCookedPointerData.pointerCoords,
-                    mCurrentCookedPointerData.idToIndex,
+                    mCurrentCookedState.cookedPointerData.pointerProperties,
+                    mCurrentCookedState.cookedPointerData.pointerCoords,
+                    mCurrentCookedState.cookedPointerData.idToIndex,
                     dispatchedIdBits, -1,
                     mOrientedXPrecision, mOrientedYPrecision, mDownTime);
         }
@@ -4104,9 +4333,9 @@
 
             dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, 0,
-                    mCurrentCookedPointerData.pointerProperties,
-                    mCurrentCookedPointerData.pointerCoords,
-                    mCurrentCookedPointerData.idToIndex,
+                    mCurrentCookedState.cookedPointerData.pointerProperties,
+                    mCurrentCookedState.cookedPointerData.pointerCoords,
+                    mCurrentCookedState.cookedPointerData.idToIndex,
                     dispatchedIdBits, downId,
                     mOrientedXPrecision, mOrientedYPrecision, mDownTime);
         }
@@ -4115,57 +4344,60 @@
 
 void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) {
     if (mSentHoverEnter &&
-            (mCurrentCookedPointerData.hoveringIdBits.isEmpty()
-                    || !mCurrentCookedPointerData.touchingIdBits.isEmpty())) {
+            (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()
+                    || !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
         int32_t metaState = getContext()->getGlobalMetaState();
         dispatchMotion(when, policyFlags, mSource,
-                AMOTION_EVENT_ACTION_HOVER_EXIT, 0, metaState, mLastButtonState, 0,
-                mLastCookedPointerData.pointerProperties,
-                mLastCookedPointerData.pointerCoords,
-                mLastCookedPointerData.idToIndex,
-                mLastCookedPointerData.hoveringIdBits, -1,
+                AMOTION_EVENT_ACTION_HOVER_EXIT, 0, metaState, mLastRawState.buttonState, 0,
+                mLastCookedState.cookedPointerData.pointerProperties,
+                mLastCookedState.cookedPointerData.pointerCoords,
+                mLastCookedState.cookedPointerData.idToIndex,
+                mLastCookedState.cookedPointerData.hoveringIdBits, -1,
                 mOrientedXPrecision, mOrientedYPrecision, mDownTime);
         mSentHoverEnter = false;
     }
 }
 
 void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) {
-    if (mCurrentCookedPointerData.touchingIdBits.isEmpty()
-            && !mCurrentCookedPointerData.hoveringIdBits.isEmpty()) {
+    if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty()
+            && !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
         if (!mSentHoverEnter) {
-            dispatchMotion(when, policyFlags, mSource,
-                    AMOTION_EVENT_ACTION_HOVER_ENTER, 0, metaState, mCurrentButtonState, 0,
-                    mCurrentCookedPointerData.pointerProperties,
-                    mCurrentCookedPointerData.pointerCoords,
-                    mCurrentCookedPointerData.idToIndex,
-                    mCurrentCookedPointerData.hoveringIdBits, -1,
+            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER,
+                    0, metaState, mCurrentRawState.buttonState, 0,
+                    mCurrentCookedState.cookedPointerData.pointerProperties,
+                    mCurrentCookedState.cookedPointerData.pointerCoords,
+                    mCurrentCookedState.cookedPointerData.idToIndex,
+                    mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
                     mOrientedXPrecision, mOrientedYPrecision, mDownTime);
             mSentHoverEnter = true;
         }
 
         dispatchMotion(when, policyFlags, mSource,
-                AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, mCurrentButtonState, 0,
-                mCurrentCookedPointerData.pointerProperties,
-                mCurrentCookedPointerData.pointerCoords,
-                mCurrentCookedPointerData.idToIndex,
-                mCurrentCookedPointerData.hoveringIdBits, -1,
+                AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState,
+                mCurrentRawState.buttonState, 0,
+                mCurrentCookedState.cookedPointerData.pointerProperties,
+                mCurrentCookedState.cookedPointerData.pointerCoords,
+                mCurrentCookedState.cookedPointerData.idToIndex,
+                mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
                 mOrientedXPrecision, mOrientedYPrecision, mDownTime);
     }
 }
 
 void TouchInputMapper::cookPointerData() {
-    uint32_t currentPointerCount = mCurrentRawPointerData.pointerCount;
+    uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;
 
-    mCurrentCookedPointerData.clear();
-    mCurrentCookedPointerData.pointerCount = currentPointerCount;
-    mCurrentCookedPointerData.hoveringIdBits = mCurrentRawPointerData.hoveringIdBits;
-    mCurrentCookedPointerData.touchingIdBits = mCurrentRawPointerData.touchingIdBits;
+    mCurrentCookedState.cookedPointerData.clear();
+    mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
+    mCurrentCookedState.cookedPointerData.hoveringIdBits =
+            mCurrentRawState.rawPointerData.hoveringIdBits;
+    mCurrentCookedState.cookedPointerData.touchingIdBits =
+            mCurrentRawState.rawPointerData.touchingIdBits;
 
     // Walk through the the active pointers and map device coordinates onto
     // surface coordinates and adjust for display orientation.
     for (uint32_t i = 0; i < currentPointerCount; i++) {
-        const RawPointerData::Pointer& in = mCurrentRawPointerData.pointers[i];
+        const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
 
         // Size
         float touchMajor, touchMinor, toolMajor, toolMinor, size;
@@ -4204,7 +4436,8 @@
             }
 
             if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) {
-                uint32_t touchingCount = mCurrentRawPointerData.touchingIdBits.count();
+                uint32_t touchingCount =
+                        mCurrentRawState.rawPointerData.touchingIdBits.count();
                 if (touchingCount > 1) {
                     touchMajor /= touchingCount;
                     touchMinor /= touchingCount;
@@ -4373,7 +4606,7 @@
         }
 
         // Write output coords.
-        PointerCoords& out = mCurrentCookedPointerData.pointerCoords[i];
+        PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
         out.clear();
         out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
         out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
@@ -4395,14 +4628,15 @@
         }
 
         // Write output properties.
-        PointerProperties& properties = mCurrentCookedPointerData.pointerProperties[i];
+        PointerProperties& properties =
+                mCurrentCookedState.cookedPointerData.pointerProperties[i];
         uint32_t id = in.id;
         properties.clear();
         properties.id = id;
         properties.toolType = in.toolType;
 
         // Write id index.
-        mCurrentCookedPointerData.idToIndex[id] = i;
+        mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
     }
 }
 
@@ -4506,7 +4740,7 @@
 
     // Send events!
     int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t buttonState = mCurrentButtonState;
+    int32_t buttonState = mCurrentRawState.buttonState;
 
     // Update last coordinates of pointers that have moved so that we observe the new
     // pointer positions at the same time as other pointers that have just gone up.
@@ -4527,7 +4761,7 @@
                 mPointerGesture.lastGestureProperties,
                 mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
                 movedGestureIdBits);
-        if (buttonState != mLastButtonState) {
+        if (buttonState != mLastRawState.buttonState) {
             moveNeeded = true;
         }
     }
@@ -4658,7 +4892,7 @@
     // Cancel previously dispatches pointers.
     if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
-        int32_t buttonState = mCurrentButtonState;
+        int32_t buttonState = mCurrentRawState.buttonState;
         dispatchMotion(when, policyFlags, mSource,
                 AMOTION_EVENT_ACTION_CANCEL, 0, metaState, buttonState,
                 AMOTION_EVENT_EDGE_FLAG_NONE,
@@ -4715,21 +4949,22 @@
         return false;
     }
 
-    const uint32_t currentFingerCount = mCurrentFingerIdBits.count();
-    const uint32_t lastFingerCount = mLastFingerIdBits.count();
+    const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
+    const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count();
 
     // Update the velocity tracker.
     {
         VelocityTracker::Position positions[MAX_POINTERS];
         uint32_t count = 0;
-        for (BitSet32 idBits(mCurrentFingerIdBits); !idBits.isEmpty(); count++) {
+        for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) {
             uint32_t id = idBits.clearFirstMarkedBit();
-            const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
+            const RawPointerData::Pointer& pointer =
+                    mCurrentRawState.rawPointerData.pointerForId(id);
             positions[count].x = pointer.x * mPointerXMovementScale;
             positions[count].y = pointer.y * mPointerYMovementScale;
         }
         mPointerGesture.velocityTracker.addMovement(when,
-                mCurrentFingerIdBits, positions);
+                mCurrentCookedState.fingerIdBits, positions);
     }
 
     // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
@@ -4749,17 +4984,17 @@
     int32_t lastActiveTouchId = mPointerGesture.activeTouchId;
     int32_t activeTouchId = lastActiveTouchId;
     if (activeTouchId < 0) {
-        if (!mCurrentFingerIdBits.isEmpty()) {
+        if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
             activeTouchChanged = true;
             activeTouchId = mPointerGesture.activeTouchId =
-                    mCurrentFingerIdBits.firstMarkedBit();
+                    mCurrentCookedState.fingerIdBits.firstMarkedBit();
             mPointerGesture.firstTouchTime = when;
         }
-    } else if (!mCurrentFingerIdBits.hasBit(activeTouchId)) {
+    } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) {
         activeTouchChanged = true;
-        if (!mCurrentFingerIdBits.isEmpty()) {
+        if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
             activeTouchId = mPointerGesture.activeTouchId =
-                    mCurrentFingerIdBits.firstMarkedBit();
+                    mCurrentCookedState.fingerIdBits.firstMarkedBit();
         } else {
             activeTouchId = mPointerGesture.activeTouchId = -1;
         }
@@ -4782,7 +5017,7 @@
                 isQuietTime = true;
             } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG
                     && currentFingerCount >= 2
-                    && !isPointerDown(mCurrentButtonState)) {
+                    && !isPointerDown(mCurrentRawState.buttonState)) {
                 // Enter quiet time when releasing the button and there are still two or more
                 // fingers down.  This may indicate that one finger was used to press the button
                 // but it has not gone up yet.
@@ -4810,7 +5045,7 @@
         mPointerGesture.currentGestureIdBits.clear();
 
         mPointerVelocityControl.reset();
-    } else if (isPointerDown(mCurrentButtonState)) {
+    } else if (isPointerDown(mCurrentRawState.buttonState)) {
         // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
         // The pointer follows the active touch point.
         // Emit DOWN, MOVE, UP events at the pointer location.
@@ -4839,7 +5074,7 @@
         if (activeTouchId >= 0 && currentFingerCount > 1) {
             int32_t bestId = -1;
             float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
-            for (BitSet32 idBits(mCurrentFingerIdBits); !idBits.isEmpty(); ) {
+            for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); ) {
                 uint32_t id = idBits.clearFirstMarkedBit();
                 float vx, vy;
                 if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
@@ -4860,11 +5095,11 @@
             }
         }
 
-        if (activeTouchId >= 0 && mLastFingerIdBits.hasBit(activeTouchId)) {
+        if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
             const RawPointerData::Pointer& currentPointer =
-                    mCurrentRawPointerData.pointerForId(activeTouchId);
+                    mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
             const RawPointerData::Pointer& lastPointer =
-                    mLastRawPointerData.pointerForId(activeTouchId);
+                    mLastRawState.rawPointerData.pointerForId(activeTouchId);
             float deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
             float deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
 
@@ -5000,11 +5235,11 @@
             mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
         }
 
-        if (mLastFingerIdBits.hasBit(activeTouchId)) {
+        if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
             const RawPointerData::Pointer& currentPointer =
-                    mCurrentRawPointerData.pointerForId(activeTouchId);
+                    mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
             const RawPointerData::Pointer& lastPointer =
-                    mLastRawPointerData.pointerForId(activeTouchId);
+                    mLastRawState.rawPointerData.pointerForId(activeTouchId);
             float deltaX = (currentPointer.x - lastPointer.x)
                     * mPointerXMovementScale;
             float deltaY = (currentPointer.y - lastPointer.y)
@@ -5109,7 +5344,7 @@
                             + mConfig.pointerGestureMultitouchSettleInterval - when)
                             * 0.000001f);
 #endif
-            mCurrentRawPointerData.getCentroidOfTouchingPointers(
+            mCurrentRawState.rawPointerData.getCentroidOfTouchingPointers(
                     &mPointerGesture.referenceTouchX,
                     &mPointerGesture.referenceTouchY);
             mPointerController->getPosition(&mPointerGesture.referenceGestureX,
@@ -5117,23 +5352,23 @@
         }
 
         // Clear the reference deltas for fingers not yet included in the reference calculation.
-        for (BitSet32 idBits(mCurrentFingerIdBits.value
+        for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value
                 & ~mPointerGesture.referenceIdBits.value); !idBits.isEmpty(); ) {
             uint32_t id = idBits.clearFirstMarkedBit();
             mPointerGesture.referenceDeltas[id].dx = 0;
             mPointerGesture.referenceDeltas[id].dy = 0;
         }
-        mPointerGesture.referenceIdBits = mCurrentFingerIdBits;
+        mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits;
 
         // Add delta for all fingers and calculate a common movement delta.
         float commonDeltaX = 0, commonDeltaY = 0;
-        BitSet32 commonIdBits(mLastFingerIdBits.value
-                & mCurrentFingerIdBits.value);
+        BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value
+                & mCurrentCookedState.fingerIdBits.value);
         for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
             bool first = (idBits == commonIdBits);
             uint32_t id = idBits.clearFirstMarkedBit();
-            const RawPointerData::Pointer& cpd = mCurrentRawPointerData.pointerForId(id);
-            const RawPointerData::Pointer& lpd = mLastRawPointerData.pointerForId(id);
+            const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id);
+            const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id);
             PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
             delta.dx += cpd.x - lpd.x;
             delta.dy += cpd.y - lpd.y;
@@ -5174,11 +5409,13 @@
                     mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
                 } else {
                     // There are exactly two pointers.
-                    BitSet32 idBits(mCurrentFingerIdBits);
+                    BitSet32 idBits(mCurrentCookedState.fingerIdBits);
                     uint32_t id1 = idBits.clearFirstMarkedBit();
                     uint32_t id2 = idBits.firstMarkedBit();
-                    const RawPointerData::Pointer& p1 = mCurrentRawPointerData.pointerForId(id1);
-                    const RawPointerData::Pointer& p2 = mCurrentRawPointerData.pointerForId(id2);
+                    const RawPointerData::Pointer& p1 =
+                            mCurrentRawState.rawPointerData.pointerForId(id1);
+                    const RawPointerData::Pointer& p2 =
+                            mCurrentRawState.rawPointerData.pointerForId(id2);
                     float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y);
                     if (mutualDistance > mPointerGestureMaxSwipeWidth) {
                         // There are two pointers but they are too far apart for a SWIPE,
@@ -5324,14 +5561,14 @@
             } else {
                 // Otherwise, assume we mapped all touches from the previous frame.
                 // Reuse all mappings that are still applicable.
-                mappedTouchIdBits.value = mLastFingerIdBits.value
-                        & mCurrentFingerIdBits.value;
+                mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value
+                        & mCurrentCookedState.fingerIdBits.value;
                 usedGestureIdBits = mPointerGesture.lastGestureIdBits;
 
                 // Check whether we need to choose a new active gesture id because the
                 // current went went up.
-                for (BitSet32 upTouchIdBits(mLastFingerIdBits.value
-                        & ~mCurrentFingerIdBits.value);
+                for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value
+                        & ~mCurrentCookedState.fingerIdBits.value);
                         !upTouchIdBits.isEmpty(); ) {
                     uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit();
                     uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
@@ -5350,7 +5587,7 @@
                     mPointerGesture.activeGestureId);
 #endif
 
-            BitSet32 idBits(mCurrentFingerIdBits);
+            BitSet32 idBits(mCurrentCookedState.fingerIdBits);
             for (uint32_t i = 0; i < currentFingerCount; i++) {
                 uint32_t touchId = idBits.clearFirstMarkedBit();
                 uint32_t gestureId;
@@ -5374,7 +5611,7 @@
                 mPointerGesture.currentGestureIdToIndex[gestureId] = i;
 
                 const RawPointerData::Pointer& pointer =
-                        mCurrentRawPointerData.pointerForId(touchId);
+                        mCurrentRawState.rawPointerData.pointerForId(touchId);
                 float deltaX = (pointer.x - mPointerGesture.referenceTouchX)
                         * mPointerXZoomScale;
                 float deltaY = (pointer.y - mPointerGesture.referenceTouchY)
@@ -5405,7 +5642,7 @@
         }
     }
 
-    mPointerController->setButtonState(mCurrentButtonState);
+    mPointerController->setButtonState(mCurrentRawState.buttonState);
 
 #if DEBUG_GESTURES
     ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
@@ -5447,23 +5684,24 @@
     mPointerSimple.currentProperties.clear();
 
     bool down, hovering;
-    if (!mCurrentStylusIdBits.isEmpty()) {
-        uint32_t id = mCurrentStylusIdBits.firstMarkedBit();
-        uint32_t index = mCurrentCookedPointerData.idToIndex[id];
-        float x = mCurrentCookedPointerData.pointerCoords[index].getX();
-        float y = mCurrentCookedPointerData.pointerCoords[index].getY();
+    if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
+        uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit();
+        uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id];
+        float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX();
+        float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY();
         mPointerController->setPosition(x, y);
 
-        hovering = mCurrentCookedPointerData.hoveringIdBits.hasBit(id);
+        hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
         down = !hovering;
 
         mPointerController->getPosition(&x, &y);
-        mPointerSimple.currentCoords.copyFrom(mCurrentCookedPointerData.pointerCoords[index]);
+        mPointerSimple.currentCoords.copyFrom(
+                mCurrentCookedState.cookedPointerData.pointerCoords[index]);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         mPointerSimple.currentProperties.id = 0;
         mPointerSimple.currentProperties.toolType =
-                mCurrentCookedPointerData.pointerProperties[index].toolType;
+                mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType;
     } else {
         down = false;
         hovering = false;
@@ -5481,16 +5719,16 @@
     mPointerSimple.currentProperties.clear();
 
     bool down, hovering;
-    if (!mCurrentMouseIdBits.isEmpty()) {
-        uint32_t id = mCurrentMouseIdBits.firstMarkedBit();
-        uint32_t currentIndex = mCurrentRawPointerData.idToIndex[id];
-        if (mLastMouseIdBits.hasBit(id)) {
-            uint32_t lastIndex = mCurrentRawPointerData.idToIndex[id];
-            float deltaX = (mCurrentRawPointerData.pointers[currentIndex].x
-                    - mLastRawPointerData.pointers[lastIndex].x)
+    if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
+        uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit();
+        uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
+        if (mLastCookedState.mouseIdBits.hasBit(id)) {
+            uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id];
+            float deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x
+                    - mLastRawState.rawPointerData.pointers[lastIndex].x)
                     * mPointerXMovementScale;
-            float deltaY = (mCurrentRawPointerData.pointers[currentIndex].y
-                    - mLastRawPointerData.pointers[lastIndex].y)
+            float deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y
+                    - mLastRawState.rawPointerData.pointers[lastIndex].y)
                     * mPointerYMovementScale;
 
             rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
@@ -5501,20 +5739,20 @@
             mPointerVelocityControl.reset();
         }
 
-        down = isPointerDown(mCurrentButtonState);
+        down = isPointerDown(mCurrentRawState.buttonState);
         hovering = !down;
 
         float x, y;
         mPointerController->getPosition(&x, &y);
         mPointerSimple.currentCoords.copyFrom(
-                mCurrentCookedPointerData.pointerCoords[currentIndex]);
+                mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
                 hovering ? 0.0f : 1.0f);
         mPointerSimple.currentProperties.id = 0;
         mPointerSimple.currentProperties.toolType =
-                mCurrentCookedPointerData.pointerProperties[currentIndex].toolType;
+                mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType;
     } else {
         mPointerVelocityControl.reset();
 
@@ -5539,7 +5777,7 @@
         if (down || hovering) {
             mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
             mPointerController->clearSpots();
-            mPointerController->setButtonState(mCurrentButtonState);
+            mPointerController->setButtonState(mCurrentRawState.buttonState);
             mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
         } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
             mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
@@ -5551,7 +5789,7 @@
 
         // Send up.
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                 AMOTION_EVENT_ACTION_UP, 0, metaState, mLastButtonState, 0,
+                 AMOTION_EVENT_ACTION_UP, 0, metaState, mLastRawState.buttonState, 0,
                  mViewport.displayId,
                  1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
                  mOrientedXPrecision, mOrientedYPrecision,
@@ -5564,7 +5802,7 @@
 
         // Send hover exit.
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                AMOTION_EVENT_ACTION_HOVER_EXIT, 0, metaState, mLastButtonState, 0,
+                AMOTION_EVENT_ACTION_HOVER_EXIT, 0, metaState, mLastRawState.buttonState, 0,
                 mViewport.displayId,
                 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
@@ -5579,7 +5817,7 @@
 
             // Send down.
             NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                    AMOTION_EVENT_ACTION_DOWN, 0, metaState, mCurrentButtonState, 0,
+                    AMOTION_EVENT_ACTION_DOWN, 0, metaState, mCurrentRawState.buttonState, 0,
                     mViewport.displayId,
                     1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                     mOrientedXPrecision, mOrientedYPrecision,
@@ -5589,7 +5827,7 @@
 
         // Send move.
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                AMOTION_EVENT_ACTION_MOVE, 0, metaState, mCurrentButtonState, 0,
+                AMOTION_EVENT_ACTION_MOVE, 0, metaState, mCurrentRawState.buttonState, 0,
                 mViewport.displayId,
                 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
@@ -5603,7 +5841,8 @@
 
             // Send hover enter.
             NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                    AMOTION_EVENT_ACTION_HOVER_ENTER, 0, metaState, mCurrentButtonState, 0,
+                    AMOTION_EVENT_ACTION_HOVER_ENTER, 0, metaState,
+                    mCurrentRawState.buttonState, 0,
                     mViewport.displayId,
                     1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                     mOrientedXPrecision, mOrientedYPrecision,
@@ -5613,7 +5852,8 @@
 
         // Send hover move.
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, mCurrentButtonState, 0,
+                AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState,
+                mCurrentRawState.buttonState, 0,
                 mViewport.displayId,
                 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
@@ -5621,9 +5861,9 @@
         getListener()->notifyMotion(&args);
     }
 
-    if (mCurrentRawVScroll || mCurrentRawHScroll) {
-        float vscroll = mCurrentRawVScroll;
-        float hscroll = mCurrentRawHScroll;
+    if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
+        float vscroll = mCurrentRawState.rawVScroll;
+        float hscroll = mCurrentRawState.rawHScroll;
         mWheelYVelocityControl.move(when, NULL, &vscroll);
         mWheelXVelocityControl.move(when, &hscroll, NULL);
 
@@ -5634,7 +5874,7 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                AMOTION_EVENT_ACTION_SCROLL, 0, metaState, mCurrentButtonState, 0,
+                AMOTION_EVENT_ACTION_SCROLL, 0, metaState, mCurrentRawState.buttonState, 0,
                 mViewport.displayId,
                 1, &mPointerSimple.currentProperties, &pointerCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
@@ -5768,11 +6008,11 @@
     return NULL;
 }
 
-void TouchInputMapper::assignPointerIds() {
-    uint32_t currentPointerCount = mCurrentRawPointerData.pointerCount;
-    uint32_t lastPointerCount = mLastRawPointerData.pointerCount;
+void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) {
+    uint32_t currentPointerCount = current->rawPointerData.pointerCount;
+    uint32_t lastPointerCount = last->rawPointerData.pointerCount;
 
-    mCurrentRawPointerData.clearIdBits();
+    current->rawPointerData.clearIdBits();
 
     if (currentPointerCount == 0) {
         // No pointers to assign.
@@ -5783,21 +6023,21 @@
         // All pointers are new.
         for (uint32_t i = 0; i < currentPointerCount; i++) {
             uint32_t id = i;
-            mCurrentRawPointerData.pointers[i].id = id;
-            mCurrentRawPointerData.idToIndex[id] = i;
-            mCurrentRawPointerData.markIdBit(id, mCurrentRawPointerData.isHovering(i));
+            current->rawPointerData.pointers[i].id = id;
+            current->rawPointerData.idToIndex[id] = i;
+            current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i));
         }
         return;
     }
 
     if (currentPointerCount == 1 && lastPointerCount == 1
-            && mCurrentRawPointerData.pointers[0].toolType
-                    == mLastRawPointerData.pointers[0].toolType) {
+            && current->rawPointerData.pointers[0].toolType
+                    == last->rawPointerData.pointers[0].toolType) {
         // Only one pointer and no change in count so it must have the same id as before.
-        uint32_t id = mLastRawPointerData.pointers[0].id;
-        mCurrentRawPointerData.pointers[0].id = id;
-        mCurrentRawPointerData.idToIndex[id] = 0;
-        mCurrentRawPointerData.markIdBit(id, mCurrentRawPointerData.isHovering(0));
+        uint32_t id = last->rawPointerData.pointers[0].id;
+        current->rawPointerData.pointers[0].id = id;
+        current->rawPointerData.idToIndex[id] = 0;
+        current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0));
         return;
     }
 
@@ -5815,9 +6055,9 @@
         for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
                 lastPointerIndex++) {
             const RawPointerData::Pointer& currentPointer =
-                    mCurrentRawPointerData.pointers[currentPointerIndex];
+                    current->rawPointerData.pointers[currentPointerIndex];
             const RawPointerData::Pointer& lastPointer =
-                    mLastRawPointerData.pointers[lastPointerIndex];
+                    last->rawPointerData.pointers[lastPointerIndex];
             if (currentPointer.toolType == lastPointer.toolType) {
                 int64_t deltaX = currentPointer.x - lastPointer.x;
                 int64_t deltaY = currentPointer.y - lastPointer.y;
@@ -5923,11 +6163,11 @@
             matchedCurrentBits.markBit(currentPointerIndex);
             matchedLastBits.markBit(lastPointerIndex);
 
-            uint32_t id = mLastRawPointerData.pointers[lastPointerIndex].id;
-            mCurrentRawPointerData.pointers[currentPointerIndex].id = id;
-            mCurrentRawPointerData.idToIndex[id] = currentPointerIndex;
-            mCurrentRawPointerData.markIdBit(id,
-                    mCurrentRawPointerData.isHovering(currentPointerIndex));
+            uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id;
+            current->rawPointerData.pointers[currentPointerIndex].id = id;
+            current->rawPointerData.idToIndex[id] = currentPointerIndex;
+            current->rawPointerData.markIdBit(id,
+                    current->rawPointerData.isHovering(currentPointerIndex));
             usedIdBits.markBit(id);
 
 #if DEBUG_POINTER_ASSIGNMENT
@@ -5943,10 +6183,10 @@
         uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit();
         uint32_t id = usedIdBits.markFirstUnmarkedBit();
 
-        mCurrentRawPointerData.pointers[currentPointerIndex].id = id;
-        mCurrentRawPointerData.idToIndex[id] = currentPointerIndex;
-        mCurrentRawPointerData.markIdBit(id,
-                mCurrentRawPointerData.isHovering(currentPointerIndex));
+        current->rawPointerData.pointers[currentPointerIndex].id = id;
+        current->rawPointerData.idToIndex[id] = currentPointerIndex;
+        current->rawPointerData.markIdBit(id,
+                current->rawPointerData.isHovering(currentPointerIndex));
 
 #if DEBUG_POINTER_ASSIGNMENT
         ALOGD("assignPointerIds - assigned: cur=%d, id=%d",
@@ -6025,18 +6265,18 @@
     mSingleTouchMotionAccumulator.process(rawEvent);
 }
 
-void SingleTouchInputMapper::syncTouch(nsecs_t when, bool* outHavePointerIds) {
+void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
     if (mTouchButtonAccumulator.isToolActive()) {
-        mCurrentRawPointerData.pointerCount = 1;
-        mCurrentRawPointerData.idToIndex[0] = 0;
+        outState->rawPointerData.pointerCount = 1;
+        outState->rawPointerData.idToIndex[0] = 0;
 
         bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE
                 && (mTouchButtonAccumulator.isHovering()
                         || (mRawPointerAxes.pressure.valid
                                 && mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0));
-        mCurrentRawPointerData.markIdBit(0, isHovering);
+        outState->rawPointerData.markIdBit(0, isHovering);
 
-        RawPointerData::Pointer& outPointer = mCurrentRawPointerData.pointers[0];
+        RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0];
         outPointer.id = 0;
         outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX();
         outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY();
@@ -6097,7 +6337,7 @@
     mMultiTouchMotionAccumulator.process(rawEvent);
 }
 
-void MultiTouchInputMapper::syncTouch(nsecs_t when, bool* outHavePointerIds) {
+void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
     size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
     size_t outCount = 0;
     BitSet32 newPointerIdBits;
@@ -6118,7 +6358,7 @@
             break; // too many fingers!
         }
 
-        RawPointerData::Pointer& outPointer = mCurrentRawPointerData.pointers[outCount];
+        RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
         outPointer.x = inSlot->getX();
         outPointer.y = inSlot->getY();
         outPointer.pressure = inSlot->getPressure();
@@ -6145,38 +6385,37 @@
         outPointer.isHovering = isHovering;
 
         // Assign pointer id using tracking id if available.
-        if (*outHavePointerIds) {
-            int32_t trackingId = inSlot->getTrackingId();
-            int32_t id = -1;
-            if (trackingId >= 0) {
-                for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
-                    uint32_t n = idBits.clearFirstMarkedBit();
-                    if (mPointerTrackingIdMap[n] == trackingId) {
-                        id = n;
-                    }
+        mHavePointerIds = true;
+        int32_t trackingId = inSlot->getTrackingId();
+        int32_t id = -1;
+        if (trackingId >= 0) {
+            for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
+                uint32_t n = idBits.clearFirstMarkedBit();
+                if (mPointerTrackingIdMap[n] == trackingId) {
+                    id = n;
                 }
+            }
 
-                if (id < 0 && !mPointerIdBits.isFull()) {
-                    id = mPointerIdBits.markFirstUnmarkedBit();
-                    mPointerTrackingIdMap[id] = trackingId;
-                }
+            if (id < 0 && !mPointerIdBits.isFull()) {
+                id = mPointerIdBits.markFirstUnmarkedBit();
+                mPointerTrackingIdMap[id] = trackingId;
             }
-            if (id < 0) {
-                *outHavePointerIds = false;
-                mCurrentRawPointerData.clearIdBits();
-                newPointerIdBits.clear();
-            } else {
-                outPointer.id = id;
-                mCurrentRawPointerData.idToIndex[id] = outCount;
-                mCurrentRawPointerData.markIdBit(id, isHovering);
-                newPointerIdBits.markBit(id);
-            }
+        }
+        if (id < 0) {
+            mHavePointerIds = false;
+            outState->rawPointerData.clearIdBits();
+            newPointerIdBits.clear();
+        } else {
+            outPointer.id = id;
+            outState->rawPointerData.idToIndex[id] = outCount;
+            outState->rawPointerData.markIdBit(id, isHovering);
+            newPointerIdBits.markBit(id);
         }
 
         outCount += 1;
     }
 
-    mCurrentRawPointerData.pointerCount = outCount;
+    outState->rawPointerData.pointerCount = outCount;
     mPointerIdBits = newPointerIdBits;
 
     mMultiTouchMotionAccumulator.finishSync();
@@ -6220,6 +6459,73 @@
             || mTouchButtonAccumulator.hasStylus();
 }
 
+// --- ExternalStylusInputMapper
+
+ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) :
+    InputMapper(device) {
+
+}
+
+uint32_t ExternalStylusInputMapper::getSources() {
+    return AINPUT_SOURCE_STYLUS;
+}
+
+void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS,
+            0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+}
+
+void ExternalStylusInputMapper::dump(String8& dump) {
+    dump.append(INDENT2 "External Stylus Input Mapper:\n");
+    dump.append(INDENT3 "Raw Stylus Axes:\n");
+    dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure");
+    dump.append(INDENT3 "Stylus State:\n");
+    dumpStylusState(dump, mStylusState);
+}
+
+void ExternalStylusInputMapper::configure(nsecs_t when,
+        const InputReaderConfiguration* config, uint32_t changes) {
+    getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
+    mTouchButtonAccumulator.configure(getDevice());
+}
+
+void ExternalStylusInputMapper::reset(nsecs_t when) {
+    InputDevice* device = getDevice();
+    mSingleTouchMotionAccumulator.reset(device);
+    mTouchButtonAccumulator.reset(device);
+    InputMapper::reset(when);
+}
+
+void ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
+    mSingleTouchMotionAccumulator.process(rawEvent);
+    mTouchButtonAccumulator.process(rawEvent);
+
+    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+        sync(rawEvent->when);
+    }
+}
+
+void ExternalStylusInputMapper::sync(nsecs_t when) {
+    mStylusState.clear();
+
+    mStylusState.when = when;
+
+    int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
+    if (mRawPressureAxis.valid) {
+        mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue;
+    } else if (mTouchButtonAccumulator.isToolActive()) {
+        mStylusState.pressure = 1.0f;
+    } else {
+        mStylusState.pressure = 0.0f;
+    }
+
+    mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
+    mStylusState.toolType = mTouchButtonAccumulator.getToolType();
+
+    mContext->dispatchExternalStylusState(mStylusState);
+}
+
 
 // --- JoystickInputMapper ---