Add support for disabling pointer gestures.

Made it possible for individual windows to disable pointer gestures
while the window has focus using a private API.

Cleaned up the InputReader configuration code to enable in-place
reconfiguration of input devices without having to reopen them all.
This change makes changing the pointer speed somewhat nicer since the
pointer doesn't jump back to the origin after each change.

Change-Id: I9727419c2f4cb39e16acb4b15fd7fd84526b1239
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 6b6aee3..d1ad113 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1004,6 +1004,23 @@
          */
         public boolean hasSystemUiListeners;
 
+        /**
+         * When this window has focus, disable touch pad pointer gesture processing.
+         * The window will receive raw position updates from the touch pad instead
+         * of pointer movements and synthetic touch events.
+         *
+         * @hide
+         */
+        public static final int INPUT_FEATURE_DISABLE_POINTER_GESTURES = 0x00000001;
+
+        /**
+         * Control special features of the input subsystem.
+         *
+         * @see #INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES
+         * @hide
+         */
+        public int inputFeatures;
+
         public LayoutParams() {
             super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
             type = TYPE_APPLICATION;
@@ -1086,6 +1103,7 @@
             out.writeInt(systemUiVisibility);
             out.writeInt(subtreeSystemUiVisibility);
             out.writeInt(hasSystemUiListeners ? 1 : 0);
+            out.writeInt(inputFeatures);
         }
         
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1124,6 +1142,7 @@
             systemUiVisibility = in.readInt();
             subtreeSystemUiVisibility = in.readInt();
             hasSystemUiListeners = in.readInt() != 0;
+            inputFeatures = in.readInt();
         }
     
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -1145,6 +1164,8 @@
         public static final int SYSTEM_UI_VISIBILITY_CHANGED = 1<<13;
         /** {@hide} */
         public static final int SYSTEM_UI_LISTENER_CHANGED = 1<<14;
+        /** {@hide} */
+        public static final int INPUT_FEATURES_CHANGED = 1<<15;
     
         // internal buffer to backup/restore parameters under compatibility mode.
         private int[] mCompatibilityParamsBackup = null;
@@ -1256,6 +1277,11 @@
                 changes |= SYSTEM_UI_LISTENER_CHANGED;
             }
 
+            if (inputFeatures != o.inputFeatures) {
+                inputFeatures = o.inputFeatures;
+                changes |= INPUT_FEATURES_CHANGED;
+            }
+
             return changes;
         }
     
@@ -1340,6 +1366,7 @@
                 sb.append(" sysuil=");
                 sb.append(hasSystemUiListeners);
             }
+            sb.append(" if=0x").append(Integer.toHexString(inputFeatures));
             sb.append('}');
             return sb.toString();
         }
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 28df3fb..4a50d8a 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -3395,6 +3395,7 @@
                     window.frameRight, window.frameBottom,
                     window.scaleFactor);
             dumpRegion(dump, window.touchableRegion);
+            dump.appendFormat(", inputFeatures=0x%08x", window.inputFeatures);
             dump.appendFormat(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
                     window.ownerPid, window.ownerUid,
                     window.dispatchingTimeout / 1000000.0);
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 2b61570..3e4c666 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -38,7 +38,6 @@
 
 #include "InputReader.h"
 
-#include <cutils/atomic.h>
 #include <cutils/log.h>
 #include <ui/Keyboard.h>
 #include <ui/VirtualKeyMap.h>
@@ -245,8 +244,8 @@
         const sp<InputDispatcherInterface>& dispatcher) :
         mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
         mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
-        mRefreshConfiguration(0) {
-    configure(true /*firstTime*/);
+        mConfigurationChangesToRefresh(0) {
+    refreshConfiguration(0);
     updateGlobalMetaState();
     updateInputConfiguration();
 }
@@ -258,9 +257,16 @@
 }
 
 void InputReader::loopOnce() {
-    if (android_atomic_acquire_load(&mRefreshConfiguration)) {
-        android_atomic_release_store(0, &mRefreshConfiguration);
-        configure(false /*firstTime*/);
+    uint32_t changes;
+    { // acquire lock
+        AutoMutex _l(mStateLock);
+
+        changes = mConfigurationChangesToRefresh;
+        mConfigurationChangesToRefresh = 0;
+    } // release lock
+
+    if (changes) {
+        refreshConfiguration(changes);
     }
 
     int32_t timeoutMillis = -1;
@@ -326,7 +332,7 @@
     uint32_t classes = mEventHub->getDeviceClasses(deviceId);
 
     InputDevice* device = createDevice(deviceId, name, classes);
-    device->configure();
+    device->configure(&mConfig, 0);
 
     if (device->isIgnored()) {
         LOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, name.string());
@@ -483,12 +489,25 @@
     mDispatcher->notifyConfigurationChanged(when);
 }
 
-void InputReader::configure(bool firstTime) {
+void InputReader::refreshConfiguration(uint32_t changes) {
     mPolicy->getReaderConfiguration(&mConfig);
     mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
 
-    if (!firstTime) {
-        mEventHub->requestReopenDevices();
+    if (changes) {
+        LOGI("Reconfiguring input devices.  changes=0x%08x", changes);
+
+        if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
+            mEventHub->requestReopenDevices();
+        } else {
+            { // acquire device registry reader lock
+                RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+                for (size_t i = 0; i < mDevices.size(); i++) {
+                    InputDevice* device = mDevices.valueAt(i);
+                    device->configure(&mConfig, changes);
+                }
+            } // release device registry reader lock
+        }
     }
 }
 
@@ -709,10 +728,20 @@
     } // release device registy reader lock
 }
 
-void InputReader::refreshConfiguration() {
-    android_atomic_release_store(1, &mRefreshConfiguration);
+void InputReader::requestRefreshConfiguration(uint32_t changes) {
+    if (changes) {
+        bool needWake;
+        { // acquire lock
+            AutoMutex _l(mStateLock);
 
-    mEventHub->wake();
+            needWake = !mConfigurationChangesToRefresh;
+            mConfigurationChangesToRefresh |= changes;
+        } // release lock
+
+        if (needWake) {
+            mEventHub->wake();
+        }
+    }
 }
 
 void InputReader::dump(String8& dump) {
@@ -760,6 +789,8 @@
             mConfig.wheelVelocityControlParameters.acceleration);
 
     dump.appendFormat(INDENT2 "PointerGesture:\n");
+    dump.appendFormat(INDENT3 "Enabled: %s\n",
+            toString(mConfig.pointerGesturesEnabled));
     dump.appendFormat(INDENT3 "QuietInterval: %0.1fms\n",
             mConfig.pointerGestureQuietInterval * 0.000001f);
     dump.appendFormat(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n",
@@ -855,18 +886,20 @@
     mMappers.add(mapper);
 }
 
-void InputDevice::configure() {
-    if (! isIgnored()) {
-        mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
-    }
-
+void InputDevice::configure(const InputReaderConfiguration* config, uint32_t changes) {
     mSources = 0;
 
-    size_t numMappers = mMappers.size();
-    for (size_t i = 0; i < numMappers; i++) {
-        InputMapper* mapper = mMappers[i];
-        mapper->configure();
-        mSources |= mapper->getSources();
+    if (!isIgnored()) {
+        if (!changes) { // first time only
+            mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
+        }
+
+        size_t numMappers = mMappers.size();
+        for (size_t i = 0; i < numMappers; i++) {
+            InputMapper* mapper = mMappers[i];
+            mapper->configure(config, changes);
+            mSources |= mapper->getSources();
+        }
     }
 }
 
@@ -1010,7 +1043,7 @@
 void InputMapper::dump(String8& dump) {
 }
 
-void InputMapper::configure() {
+void InputMapper::configure(const InputReaderConfiguration* config, uint32_t changes) {
 }
 
 void InputMapper::reset() {
@@ -1124,16 +1157,18 @@
 }
 
 
-void KeyboardInputMapper::configure() {
-    InputMapper::configure();
+void KeyboardInputMapper::configure(const InputReaderConfiguration* config, uint32_t changes) {
+    InputMapper::configure(config, changes);
 
-    // Configure basic parameters.
-    configureParameters();
+    if (!changes) { // first time only
+        // Configure basic parameters.
+        configureParameters();
 
-    // Reset LEDs.
-    {
-        AutoMutex _l(mLock);
-        resetLedStateLocked();
+        // Reset LEDs.
+        {
+            AutoMutex _l(mLock);
+            resetLedStateLocked();
+        }
     }
 }
 
@@ -1411,40 +1446,44 @@
     } // release lock
 }
 
-void CursorInputMapper::configure() {
-    InputMapper::configure();
+void CursorInputMapper::configure(const InputReaderConfiguration* config, uint32_t changes) {
+    InputMapper::configure(config, changes);
 
-    // Configure basic parameters.
-    configureParameters();
+    if (!changes) { // first time only
+        // Configure basic parameters.
+        configureParameters();
 
-    // Configure device mode.
-    switch (mParameters.mode) {
-    case Parameters::MODE_POINTER:
-        mSource = AINPUT_SOURCE_MOUSE;
-        mXPrecision = 1.0f;
-        mYPrecision = 1.0f;
-        mXScale = 1.0f;
-        mYScale = 1.0f;
-        mPointerController = getPolicy()->obtainPointerController(getDeviceId());
-        break;
-    case Parameters::MODE_NAVIGATION:
-        mSource = AINPUT_SOURCE_TRACKBALL;
-        mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
-        mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
-        mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
-        mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
-        break;
+        // Configure device mode.
+        switch (mParameters.mode) {
+        case Parameters::MODE_POINTER:
+            mSource = AINPUT_SOURCE_MOUSE;
+            mXPrecision = 1.0f;
+            mYPrecision = 1.0f;
+            mXScale = 1.0f;
+            mYScale = 1.0f;
+            mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+            break;
+        case Parameters::MODE_NAVIGATION:
+            mSource = AINPUT_SOURCE_TRACKBALL;
+            mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+            mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+            mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+            mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+            break;
+        }
+
+        mVWheelScale = 1.0f;
+        mHWheelScale = 1.0f;
+
+        mHaveVWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_WHEEL);
+        mHaveHWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_HWHEEL);
     }
 
-    mVWheelScale = 1.0f;
-    mHWheelScale = 1.0f;
-
-    mHaveVWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_WHEEL);
-    mHaveHWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_HWHEEL);
-
-    mPointerVelocityControl.setParameters(getConfig()->pointerVelocityControlParameters);
-    mWheelXVelocityControl.setParameters(getConfig()->wheelVelocityControlParameters);
-    mWheelYVelocityControl.setParameters(getConfig()->wheelVelocityControlParameters);
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
+        mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters);
+        mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters);
+        mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters);
+    }
 }
 
 void CursorInputMapper::configureParameters() {
@@ -1787,8 +1826,6 @@
 
 TouchInputMapper::TouchInputMapper(InputDevice* device) :
         InputMapper(device) {
-    mConfig = getConfig();
-
     mLocked.surfaceOrientation = -1;
     mLocked.surfaceWidth = -1;
     mLocked.surfaceHeight = -1;
@@ -1925,52 +1962,65 @@
     mLocked.orientedRanges.haveDistance = false;
 
     mPointerGesture.reset();
-    mPointerGesture.pointerVelocityControl.setParameters(mConfig->pointerVelocityControlParameters);
 }
 
-void TouchInputMapper::configure() {
-    InputMapper::configure();
+void TouchInputMapper::configure(const InputReaderConfiguration* config, uint32_t changes) {
+    InputMapper::configure(config, changes);
 
-    // Configure basic parameters.
-    configureParameters();
+    mConfig = *config;
 
-    // Configure sources.
-    switch (mParameters.deviceType) {
-    case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
-        mTouchSource = AINPUT_SOURCE_TOUCHSCREEN;
-        mPointerSource = 0;
-        break;
-    case Parameters::DEVICE_TYPE_TOUCH_PAD:
-        mTouchSource = AINPUT_SOURCE_TOUCHPAD;
-        mPointerSource = 0;
-        break;
-    case Parameters::DEVICE_TYPE_POINTER:
-        mTouchSource = AINPUT_SOURCE_TOUCHPAD;
-        mPointerSource = AINPUT_SOURCE_MOUSE;
-        break;
-    default:
-        LOG_ASSERT(false);
+    if (!changes) { // first time only
+        // Configure basic parameters.
+        configureParameters();
+
+        // Configure sources.
+        switch (mParameters.deviceType) {
+        case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
+            mTouchSource = AINPUT_SOURCE_TOUCHSCREEN;
+            mPointerSource = 0;
+            break;
+        case Parameters::DEVICE_TYPE_TOUCH_PAD:
+            mTouchSource = AINPUT_SOURCE_TOUCHPAD;
+            mPointerSource = 0;
+            break;
+        case Parameters::DEVICE_TYPE_POINTER:
+            mTouchSource = AINPUT_SOURCE_TOUCHPAD;
+            mPointerSource = AINPUT_SOURCE_MOUSE;
+            break;
+        default:
+            LOG_ASSERT(false);
+        }
+
+        // Configure absolute axis information.
+        configureRawAxes();
+
+        // Prepare input device calibration.
+        parseCalibration();
+        resolveCalibration();
+
+        { // acquire lock
+            AutoMutex _l(mLock);
+
+             // Configure surface dimensions and orientation.
+            configureSurfaceLocked();
+        } // release lock
     }
 
-    // Configure absolute axis information.
-    configureRawAxes();
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
+        mPointerGesture.pointerVelocityControl.setParameters(
+                mConfig.pointerVelocityControlParameters);
+    }
 
-    // Prepare input device calibration.
-    parseCalibration();
-    resolveCalibration();
-
-    { // acquire lock
-        AutoMutex _l(mLock);
-
-         // Configure surface dimensions and orientation.
-        configureSurfaceLocked();
-    } // release lock
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT)) {
+        // Reset the touch screen when pointer gesture enablement changes.
+        reset();
+    }
 }
 
 void TouchInputMapper::configureParameters() {
-    mParameters.useBadTouchFilter = mConfig->filterTouchEvents;
-    mParameters.useAveragingTouchFilter = mConfig->filterTouchEvents;
-    mParameters.useJumpyTouchFilter = mConfig->filterJumpyTouchEvents;
+    mParameters.useBadTouchFilter = mConfig.filterTouchEvents;
+    mParameters.useAveragingTouchFilter = mConfig.filterTouchEvents;
+    mParameters.useJumpyTouchFilter = mConfig.filterJumpyTouchEvents;
 
     // Use the pointer presentation mode for devices that do not support distinct
     // multitouch.  The spot-based presentation relies on being able to accurately
@@ -2401,14 +2451,14 @@
             // is applied.
             // Assume that the touch pad has a square aspect ratio such that movements in
             // X and Y of the same number of raw units cover the same physical distance.
-            mLocked.pointerGestureXMovementScale = mConfig->pointerGestureMovementSpeedRatio
+            mLocked.pointerGestureXMovementScale = mConfig.pointerGestureMovementSpeedRatio
                     * displayDiagonal / rawDiagonal;
             mLocked.pointerGestureYMovementScale = mLocked.pointerGestureXMovementScale;
 
             // Scale zooms to cover a smaller range of the display than movements do.
             // This value determines the area around the pointer that is affected by freeform
             // pointer gestures.
-            mLocked.pointerGestureXZoomScale = mConfig->pointerGestureZoomSpeedRatio
+            mLocked.pointerGestureXZoomScale = mConfig.pointerGestureZoomSpeedRatio
                     * displayDiagonal / rawDiagonal;
             mLocked.pointerGestureYZoomScale = mLocked.pointerGestureXZoomScale;
 
@@ -2416,7 +2466,7 @@
             // of the diagonal axis of the touch pad.  Touches that are wider than this are
             // translated into freeform gestures.
             mLocked.pointerGestureMaxSwipeWidth =
-                    mConfig->pointerGestureSwipeMaxWidthRatio * rawDiagonal;
+                    mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
 
             // Reset the current pointer gesture.
             mPointerGesture.reset();
@@ -2902,6 +2952,7 @@
 
         if (mPointerController != NULL
                 && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
             mPointerController->clearSpots();
         }
     } // release lock
@@ -2982,7 +3033,7 @@
         touchResult = consumeOffScreenTouches(when, policyFlags);
         if (touchResult == DISPATCH_TOUCH) {
             suppressSwipeOntoVirtualKeys(when);
-            if (mPointerController != NULL) {
+            if (mPointerController != NULL && mConfig.pointerGesturesEnabled) {
                 dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
             }
             dispatchTouches(when, policyFlags);
@@ -3139,8 +3190,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 && mCurrentTouch.pointerCount != 0) {
-        mContext->disableVirtualKeysUntil(when + mConfig->virtualKeyQuietTime);
+    if (mConfig.virtualKeyQuietTime > 0 && mCurrentTouch.pointerCount != 0) {
+        mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
     }
 }
 
@@ -3688,10 +3739,10 @@
 #endif
 
         if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
-            if (when <= mPointerGesture.tapUpTime + mConfig->pointerGestureTapDragInterval) {
+            if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
                 // The tap/drag timeout has not yet expired.
                 getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime
-                        + mConfig->pointerGestureTapDragInterval);
+                        + mConfig.pointerGestureTapDragInterval);
             } else {
                 // The tap is finished.
 #if DEBUG_GESTURES
@@ -3756,7 +3807,7 @@
     if (activeTouchId < 0) {
         mPointerGesture.resetQuietTime();
     } else {
-        isQuietTime = when < mPointerGesture.quietTime + mConfig->pointerGestureQuietInterval;
+        isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
         if (!isQuietTime) {
             if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS
                     || mPointerGesture.lastGestureMode == PointerGesture::SWIPE
@@ -3785,7 +3836,7 @@
         // Case 1: Quiet time. (QUIET)
 #if DEBUG_GESTURES
         LOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime
-                + mConfig->pointerGestureQuietInterval - when) * 0.000001f);
+                + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
 #endif
         if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
             *outFinishPreviousGesture = true;
@@ -3824,7 +3875,7 @@
         // Find the fastest pointer and follow it.
         if (activeTouchId >= 0 && mCurrentTouch.pointerCount > 1) {
             int32_t bestId = -1;
-            float bestSpeed = mConfig->pointerGestureDragMinSwitchSpeed;
+            float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
             for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
                 uint32_t id = mCurrentTouch.pointers[i].id;
                 float vx, vy;
@@ -3893,18 +3944,18 @@
         if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER
                 || mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG)
                 && mLastTouch.pointerCount == 1) {
-            if (when <= mPointerGesture.tapDownTime + mConfig->pointerGestureTapInterval) {
+            if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
                 float x, y;
                 mPointerController->getPosition(&x, &y);
-                if (fabs(x - mPointerGesture.tapX) <= mConfig->pointerGestureTapSlop
-                        && fabs(y - mPointerGesture.tapY) <= mConfig->pointerGestureTapSlop) {
+                if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop
+                        && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
 #if DEBUG_GESTURES
                     LOGD("Gestures: TAP");
 #endif
 
                     mPointerGesture.tapUpTime = when;
                     getContext()->requestTimeoutAtTime(when
-                            + mConfig->pointerGestureTapDragInterval);
+                            + mConfig.pointerGestureTapDragInterval);
 
                     mPointerGesture.activeGestureId = 0;
                     mPointerGesture.currentGestureMode = PointerGesture::TAP;
@@ -3961,11 +4012,11 @@
 
         mPointerGesture.currentGestureMode = PointerGesture::HOVER;
         if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
-            if (when <= mPointerGesture.tapUpTime + mConfig->pointerGestureTapDragInterval) {
+            if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
                 float x, y;
                 mPointerController->getPosition(&x, &y);
-                if (fabs(x - mPointerGesture.tapX) <= mConfig->pointerGestureTapSlop
-                        && fabs(y - mPointerGesture.tapY) <= mConfig->pointerGestureTapSlop) {
+                if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop
+                        && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
                     mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
                 } else {
 #if DEBUG_GESTURES
@@ -4059,7 +4110,7 @@
         LOG_ASSERT(activeTouchId >= 0);
 
         bool settled = when >= mPointerGesture.firstTouchTime
-                + mConfig->pointerGestureMultitouchSettleInterval;
+                + mConfig.pointerGestureMultitouchSettleInterval;
         if (mPointerGesture.lastGestureMode != PointerGesture::PRESS
                 && mPointerGesture.lastGestureMode != PointerGesture::SWIPE
                 && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
@@ -4070,7 +4121,7 @@
 #if DEBUG_GESTURES
             LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
                     "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
-                            + mConfig->pointerGestureMultitouchSettleInterval - when)
+                            + mConfig.pointerGestureMultitouchSettleInterval - when)
                             * 0.000001f);
 #endif
             *outCancelPreviousGesture = true;
@@ -4089,7 +4140,7 @@
 #if DEBUG_GESTURES
             LOGD("Gestures: Using centroid as reference for MULTITOUCH, "
                     "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
-                            + mConfig->pointerGestureMultitouchSettleInterval - when)
+                            + mConfig.pointerGestureMultitouchSettleInterval - when)
                             * 0.000001f);
 #endif
             mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
@@ -4143,7 +4194,7 @@
                 PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
                 dist[id] = hypotf(delta.dx * mLocked.pointerGestureXZoomScale,
                         delta.dy * mLocked.pointerGestureYZoomScale);
-                if (dist[id] > mConfig->pointerGestureMultitouchMinDistance) {
+                if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) {
                     distOverThreshold += 1;
                 }
             }
@@ -4179,8 +4230,8 @@
                     uint32_t id2 = mCurrentTouch.pointers[1].id;
                     float dist1 = dist[id1];
                     float dist2 = dist[id2];
-                    if (dist1 >= mConfig->pointerGestureMultitouchMinDistance
-                            && dist2 >= mConfig->pointerGestureMultitouchMinDistance) {
+                    if (dist1 >= mConfig.pointerGestureMultitouchMinDistance
+                            && dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
                         // Calculate the dot product of the displacement vectors.
                         // When the vectors are oriented in approximately the same direction,
                         // the angle betweeen them is near zero and the cosine of the angle
@@ -4193,15 +4244,15 @@
                         float dy2 = delta2.dy * mLocked.pointerGestureYZoomScale;
                         float dot = dx1 * dx2 + dy1 * dy2;
                         float cosine = dot / (dist1 * dist2); // denominator always > 0
-                        if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) {
+                        if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
                             // Pointers are moving in the same direction.  Switch to SWIPE.
 #if DEBUG_GESTURES
                             LOGD("Gestures: PRESS transitioned to SWIPE, "
                                     "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
                                     "cosine %0.3f >= %0.3f",
-                                    dist1, mConfig->pointerGestureMultitouchMinDistance,
-                                    dist2, mConfig->pointerGestureMultitouchMinDistance,
-                                    cosine, mConfig->pointerGestureSwipeTransitionAngleCosine);
+                                    dist1, mConfig.pointerGestureMultitouchMinDistance,
+                                    dist2, mConfig.pointerGestureMultitouchMinDistance,
+                                    cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
 #endif
                             mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
                         } else {
@@ -4210,9 +4261,9 @@
                             LOGD("Gestures: PRESS transitioned to FREEFORM, "
                                     "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
                                     "cosine %0.3f < %0.3f",
-                                    dist1, mConfig->pointerGestureMultitouchMinDistance,
-                                    dist2, mConfig->pointerGestureMultitouchMinDistance,
-                                    cosine, mConfig->pointerGestureSwipeTransitionAngleCosine);
+                                    dist1, mConfig.pointerGestureMultitouchMinDistance,
+                                    dist2, mConfig.pointerGestureMultitouchMinDistance,
+                                    cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
 #endif
                             *outCancelPreviousGesture = true;
                             mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
@@ -5665,85 +5716,87 @@
     }
 }
 
-void JoystickInputMapper::configure() {
-    InputMapper::configure();
+void JoystickInputMapper::configure(const InputReaderConfiguration* config, uint32_t changes) {
+    InputMapper::configure(config, changes);
 
-    // Collect all axes.
-    for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
-        RawAbsoluteAxisInfo rawAxisInfo;
-        getEventHub()->getAbsoluteAxisInfo(getDeviceId(), abs, &rawAxisInfo);
-        if (rawAxisInfo.valid) {
-            // Map axis.
-            AxisInfo axisInfo;
-            bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo);
-            if (!explicitlyMapped) {
-                // Axis is not explicitly mapped, will choose a generic axis later.
-                axisInfo.mode = AxisInfo::MODE_NORMAL;
-                axisInfo.axis = -1;
+    if (!changes) { // first time only
+        // Collect all axes.
+        for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
+            RawAbsoluteAxisInfo rawAxisInfo;
+            getEventHub()->getAbsoluteAxisInfo(getDeviceId(), abs, &rawAxisInfo);
+            if (rawAxisInfo.valid) {
+                // Map axis.
+                AxisInfo axisInfo;
+                bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo);
+                if (!explicitlyMapped) {
+                    // Axis is not explicitly mapped, will choose a generic axis later.
+                    axisInfo.mode = AxisInfo::MODE_NORMAL;
+                    axisInfo.axis = -1;
+                }
+
+                // Apply flat override.
+                int32_t rawFlat = axisInfo.flatOverride < 0
+                        ? rawAxisInfo.flat : axisInfo.flatOverride;
+
+                // Calculate scaling factors and limits.
+                Axis axis;
+                if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
+                    float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
+                    float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
+                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
+                            scale, 0.0f, highScale, 0.0f,
+                            0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+                } else if (isCenteredAxis(axisInfo.axis)) {
+                    float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+                    float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
+                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
+                            scale, offset, scale, offset,
+                            -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+                } else {
+                    float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
+                            scale, 0.0f, scale, 0.0f,
+                            0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+                }
+
+                // To eliminate noise while the joystick is at rest, filter out small variations
+                // in axis values up front.
+                axis.filter = axis.flat * 0.25f;
+
+                mAxes.add(abs, axis);
             }
-
-            // Apply flat override.
-            int32_t rawFlat = axisInfo.flatOverride < 0
-                    ? rawAxisInfo.flat : axisInfo.flatOverride;
-
-            // Calculate scaling factors and limits.
-            Axis axis;
-            if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
-                float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
-                float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
-                axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
-                        scale, 0.0f, highScale, 0.0f,
-                        0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
-            } else if (isCenteredAxis(axisInfo.axis)) {
-                float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
-                float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
-                axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
-                        scale, offset, scale, offset,
-                        -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
-            } else {
-                float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
-                axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
-                        scale, 0.0f, scale, 0.0f,
-                        0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
-            }
-
-            // To eliminate noise while the joystick is at rest, filter out small variations
-            // in axis values up front.
-            axis.filter = axis.flat * 0.25f;
-
-            mAxes.add(abs, axis);
         }
-    }
 
-    // If there are too many axes, start dropping them.
-    // Prefer to keep explicitly mapped axes.
-    if (mAxes.size() > PointerCoords::MAX_AXES) {
-        LOGI("Joystick '%s' has %d axes but the framework only supports a maximum of %d.",
-                getDeviceName().string(), mAxes.size(), PointerCoords::MAX_AXES);
-        pruneAxes(true);
-        pruneAxes(false);
-    }
+        // If there are too many axes, start dropping them.
+        // Prefer to keep explicitly mapped axes.
+        if (mAxes.size() > PointerCoords::MAX_AXES) {
+            LOGI("Joystick '%s' has %d axes but the framework only supports a maximum of %d.",
+                    getDeviceName().string(), mAxes.size(), PointerCoords::MAX_AXES);
+            pruneAxes(true);
+            pruneAxes(false);
+        }
 
-    // Assign generic axis ids to remaining axes.
-    int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
-    size_t numAxes = mAxes.size();
-    for (size_t i = 0; i < numAxes; i++) {
-        Axis& axis = mAxes.editValueAt(i);
-        if (axis.axisInfo.axis < 0) {
-            while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16
-                    && haveAxis(nextGenericAxisId)) {
-                nextGenericAxisId += 1;
-            }
+        // Assign generic axis ids to remaining axes.
+        int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
+        size_t numAxes = mAxes.size();
+        for (size_t i = 0; i < numAxes; i++) {
+            Axis& axis = mAxes.editValueAt(i);
+            if (axis.axisInfo.axis < 0) {
+                while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16
+                        && haveAxis(nextGenericAxisId)) {
+                    nextGenericAxisId += 1;
+                }
 
-            if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
-                axis.axisInfo.axis = nextGenericAxisId;
-                nextGenericAxisId += 1;
-            } else {
-                LOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
-                        "have already been assigned to other axes.",
-                        getDeviceName().string(), mAxes.keyAt(i));
-                mAxes.removeItemsAt(i--);
-                numAxes -= 1;
+                if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
+                    axis.axisInfo.axis = nextGenericAxisId;
+                    nextGenericAxisId += 1;
+                } else {
+                    LOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
+                            "have already been assigned to other axes.",
+                            getDeviceName().string(), mAxes.keyAt(i));
+                    mAxes.removeItemsAt(i--);
+                    numAxes -= 1;
+                }
             }
         }
     }
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 55315f7..288ff4e 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -45,6 +45,18 @@
  * Specifies various options that modify the behavior of the input reader.
  */
 struct InputReaderConfiguration {
+    // Describes changes that have occurred.
+    enum {
+        // The pointer speed changed.
+        CHANGE_POINTER_SPEED = 1 << 0,
+
+        // The pointer gesture control changed.
+        CHANGE_POINTER_GESTURE_ENABLEMENT = 1 << 1,
+
+        // All devices must be reopened.
+        CHANGE_MUST_REOPEN = 1 << 31,
+    };
+
     // Determines whether to turn on some hacks we have to improve the touch interaction with a
     // certain device whose screen currently is not all that good.
     bool filterTouchEvents;
@@ -68,6 +80,9 @@
     // Velocity control parameters for mouse wheel movements.
     VelocityControlParameters wheelVelocityControlParameters;
 
+    // True if pointer gestures are enabled.
+    bool pointerGesturesEnabled;
+
     // Quiet time between certain pointer gesture transitions.
     // Time to allow for all fingers or buttons to settle into a stable state before
     // starting a new gesture.
@@ -136,6 +151,7 @@
             virtualKeyQuietTime(0),
             pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
             wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
+            pointerGesturesEnabled(true),
             pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
             pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
             pointerGestureTapInterval(150 * 1000000LL), // 150 ms
@@ -235,8 +251,10 @@
     virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
             size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
 
-    /* Reopens and reconfigures all input devices. */
-    virtual void refreshConfiguration() = 0;
+    /* Requests that a reconfiguration of all input devices.
+     * The changes flag is a bitfield that indicates what has changed and whether
+     * the input devices must all be reopened. */
+    virtual void requestRefreshConfiguration(uint32_t changes) = 0;
 };
 
 
@@ -260,7 +278,6 @@
     virtual void requestTimeoutAtTime(nsecs_t when) = 0;
 
     virtual InputReaderPolicyInterface* getPolicy() = 0;
-    virtual const InputReaderConfiguration* getConfig() = 0;
     virtual InputDispatcherInterface* getDispatcher() = 0;
     virtual EventHubInterface* getEventHub() = 0;
 };
@@ -301,7 +318,7 @@
     virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
             size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
 
-    virtual void refreshConfiguration();
+    virtual void requestRefreshConfiguration(uint32_t changes);
 
 protected:
     // These methods are protected virtual so they can be overridden and instrumented
@@ -316,7 +333,6 @@
     InputReaderConfiguration mConfig;
 
     virtual InputReaderPolicyInterface* getPolicy() { return mPolicy.get(); }
-    virtual const InputReaderConfiguration* getConfig() { return &mConfig; }
     virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); }
     virtual EventHubInterface* getEventHub() { return mEventHub.get(); }
 
@@ -365,8 +381,8 @@
     nsecs_t mNextTimeout; // only accessed by reader thread, not guarded
     virtual void requestTimeoutAtTime(nsecs_t when);
 
-    volatile int32_t mRefreshConfiguration; // atomic
-    void configure(bool firstTime);
+    uint32_t mConfigurationChangesToRefresh; // guarded by mStateLock
+    void refreshConfiguration(uint32_t changes);
 
     // state queries
     typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
@@ -408,7 +424,7 @@
 
     void dump(String8& dump);
     void addMapper(InputMapper* mapper);
-    void configure();
+    void configure(const InputReaderConfiguration* config, uint32_t changes);
     void reset();
     void process(const RawEvent* rawEvents, size_t count);
     void timeoutExpired(nsecs_t when);
@@ -460,14 +476,13 @@
     inline const String8 getDeviceName() { return mDevice->getName(); }
     inline InputReaderContext* getContext() { return mContext; }
     inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
-    inline const InputReaderConfiguration* getConfig() { return mContext->getConfig(); }
     inline InputDispatcherInterface* getDispatcher() { return mContext->getDispatcher(); }
     inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
 
     virtual uint32_t getSources() = 0;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
     virtual void dump(String8& dump);
-    virtual void configure();
+    virtual void configure(const InputReaderConfiguration* config, uint32_t changes);
     virtual void reset();
     virtual void process(const RawEvent* rawEvent) = 0;
     virtual void timeoutExpired(nsecs_t when);
@@ -514,7 +529,7 @@
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
     virtual void dump(String8& dump);
-    virtual void configure();
+    virtual void configure(const InputReaderConfiguration* config, uint32_t changes);
     virtual void reset();
     virtual void process(const RawEvent* rawEvent);
 
@@ -584,7 +599,7 @@
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
     virtual void dump(String8& dump);
-    virtual void configure();
+    virtual void configure(const InputReaderConfiguration* config, uint32_t changes);
     virtual void reset();
     virtual void process(const RawEvent* rawEvent);
 
@@ -675,7 +690,7 @@
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
     virtual void dump(String8& dump);
-    virtual void configure();
+    virtual void configure(const InputReaderConfiguration* config, uint32_t changes);
     virtual void reset();
 
     virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
@@ -783,7 +798,7 @@
     uint32_t mPointerSource; // sources when reporting pointer gestures
 
     // The reader's configuration.
-    const InputReaderConfiguration* mConfig;
+    InputReaderConfiguration mConfig;
 
     // Immutable configuration parameters.
     struct Parameters {
@@ -1400,7 +1415,7 @@
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
     virtual void dump(String8& dump);
-    virtual void configure();
+    virtual void configure(const InputReaderConfiguration* config, uint32_t changes);
     virtual void reset();
     virtual void process(const RawEvent* rawEvent);
 
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index 93c9b5f..d166ad4 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -127,6 +127,10 @@
         LAST_SYSTEM_WINDOW      = 2999,
     };
 
+    enum {
+        INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
+    };
+
     sp<InputWindowHandle> inputWindowHandle;
     sp<InputChannel> inputChannel;
     String8 name;
@@ -147,6 +151,7 @@
     int32_t layer;
     int32_t ownerPid;
     int32_t ownerUid;
+    int32_t inputFeatures;
 
     bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
     bool frameContainsPoint(int32_t x, int32_t y) const;
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 4a32341..e349248 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -747,8 +747,6 @@
     int32_t mGlobalMetaState;
     bool mUpdateGlobalMetaStateWasCalled;
 
-    InputReaderConfiguration mConfig;
-
 public:
     FakeInputReaderContext(const sp<EventHubInterface>& eventHub,
             const sp<InputReaderPolicyInterface>& policy,
@@ -786,11 +784,6 @@
         return mPolicy.get();
     }
 
-    virtual const InputReaderConfiguration* getConfig() {
-        mPolicy->getReaderConfiguration(&mConfig);
-        return &mConfig;
-    }
-
     virtual InputDispatcherInterface* getDispatcher() {
         return mDispatcher.get();
     }
@@ -895,7 +888,7 @@
         }
     }
 
-    virtual void configure() {
+    virtual void configure(const InputReaderConfiguration* config, uint32_t changes) {
         mConfigureWasCalled = true;
     }
 
@@ -1359,7 +1352,8 @@
 
 TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
     // Configuration.
-    mDevice->configure();
+    InputReaderConfiguration config;
+    mDevice->configure(&config, 0);
 
     // Metadata.
     ASSERT_TRUE(mDevice->isIgnored());
@@ -1413,7 +1407,8 @@
     mapper2->setMetaState(AMETA_SHIFT_ON);
     mDevice->addMapper(mapper2);
 
-    mDevice->configure();
+    InputReaderConfiguration config;
+    mDevice->configure(&config, 0);
 
     String8 propertyValue;
     ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty(String8("key"), propertyValue))
@@ -1519,8 +1514,10 @@
     }
 
     void addMapperAndConfigure(InputMapper* mapper) {
+        InputReaderConfiguration config;
+
         mDevice->addMapper(mapper);
-        mDevice->configure();
+        mDevice->configure(&config, 0);
     }
 
     static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type,
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index 9dc92d3..3be8af6 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -128,6 +128,8 @@
         inputWindow.layer = mService.mDragState.getDragLayerLw();
         inputWindow.ownerPid = Process.myPid();
         inputWindow.ownerUid = Process.myUid();
+        inputWindow.inputFeatures = 0;
+        inputWindow.scaleFactor = 1.0f;
 
         // The drag window covers the entire display
         inputWindow.frameLeft = 0;
@@ -204,7 +206,8 @@
             inputWindow.layer = child.mLayer;
             inputWindow.ownerPid = child.mSession.mPid;
             inputWindow.ownerUid = child.mSession.mUid;
-            
+            inputWindow.inputFeatures = child.mAttrs.inputFeatures;
+
             final Rect frame = child.mFrame;
             inputWindow.frameLeft = frame.left;
             inputWindow.frameTop = frame.top;
diff --git a/services/java/com/android/server/wm/InputWindow.java b/services/java/com/android/server/wm/InputWindow.java
index 578120e..655d734 100644
--- a/services/java/com/android/server/wm/InputWindow.java
+++ b/services/java/com/android/server/wm/InputWindow.java
@@ -75,6 +75,9 @@
     public int ownerPid;
     public int ownerUid;
 
+    // Window input features.
+    public int inputFeatures;
+
     public void recycle() {
         inputWindowHandle = null;
         inputChannel = null;
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 7c5084f..8a46ab0 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -234,6 +234,9 @@
         // Pointer speed.
         int32_t pointerSpeed;
 
+        // True if pointer gestures are enabled.
+        bool pointerGesturesEnabled;
+
         // Sprite controller singleton, created on first use.
         sp<SpriteController> spriteController;
 
@@ -274,6 +277,7 @@
 
         mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
         mLocked.pointerSpeed = 0;
+        mLocked.pointerGesturesEnabled = true;
     }
 
     sp<EventHub> eventHub = new EventHub();
@@ -443,6 +447,7 @@
 
         outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed
                 * POINTER_SPEED_EXPONENT);
+        outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled;
     } // release lock
 }
 
@@ -594,6 +599,7 @@
 void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
     Vector<InputWindow> windows;
 
+    bool newPointerGesturesEnabled = true;
     jsize length = env->GetArrayLength(windowObjArray);
     for (jsize i = 0; i < length; i++) {
         jobject windowObj = env->GetObjectArrayElement(windowObjArray, i);
@@ -606,11 +612,29 @@
         android_server_InputWindow_toNative(env, windowObj, &window);
         if (window.inputChannel == NULL) {
             windows.pop();
+        } else if (window.hasFocus) {
+            if (window.inputFeatures & InputWindow::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES) {
+                newPointerGesturesEnabled = false;
+            }
         }
         env->DeleteLocalRef(windowObj);
     }
 
     mInputManager->getDispatcher()->setInputWindows(windows);
+
+    uint32_t changes = 0;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        if (mLocked.pointerGesturesEnabled != newPointerGesturesEnabled) {
+            mLocked.pointerGesturesEnabled = newPointerGesturesEnabled;
+            changes |= InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT;
+        }
+    } // release lock
+
+    if (changes) {
+        mInputManager->getReader()->requestRefreshConfiguration(changes);
+    }
 }
 
 void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationObj) {
@@ -650,14 +674,19 @@
 }
 
 void NativeInputManager::setPointerSpeed(int32_t speed) {
-    AutoMutex _l(mLock);
+    { // acquire lock
+        AutoMutex _l(mLock);
 
-    if (mLocked.pointerSpeed != speed) {
+        if (mLocked.pointerSpeed == speed) {
+            return;
+        }
+
         LOGI("Setting pointer speed to %d.", speed);
         mLocked.pointerSpeed = speed;
+    } // release lock
 
-        mInputManager->getReader()->refreshConfiguration();
-    }
+    mInputManager->getReader()->requestRefreshConfiguration(
+            InputReaderConfiguration::CHANGE_POINTER_SPEED);
 }
 
 bool NativeInputManager::isScreenOn() {
diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp
index 012ce21..0426f63 100644
--- a/services/jni/com_android_server_InputWindow.cpp
+++ b/services/jni/com_android_server_InputWindow.cpp
@@ -48,6 +48,7 @@
     jfieldID layer;
     jfieldID ownerPid;
     jfieldID ownerUid;
+    jfieldID inputFeatures;
 } gInputWindowClassInfo;
 
 
@@ -130,6 +131,8 @@
             gInputWindowClassInfo.ownerPid);
     outInputWindow->ownerUid = env->GetIntField(inputWindowObj,
             gInputWindowClassInfo.ownerUid);
+    outInputWindow->inputFeatures = env->GetIntField(inputWindowObj,
+            gInputWindowClassInfo.inputFeatures);
 }
 
 
@@ -206,6 +209,9 @@
 
     GET_FIELD_ID(gInputWindowClassInfo.ownerUid, clazz,
             "ownerUid", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.inputFeatures, clazz,
+            "inputFeatures", "I");
     return 0;
 }