Merge "DO NOT MERGE Use unchecked admin lookup for getStorageEncryption()" into honeycomb-mr2
diff --git a/api/current.xml b/api/current.xml
index 83e571d..acea195 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1024,6 +1024,17 @@
  visibility="public"
 >
 </field>
+<field name="SET_POINTER_SPEED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.SET_POINTER_SPEED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SET_PREFERRED_APPLICATIONS"
  type="java.lang.String"
  transient="false"
@@ -30616,6 +30627,19 @@
 <parameter name="hasMenu" type="boolean">
 </parameter>
 </method>
+<method name="setInitialSavedState"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="android.app.Fragment.SavedState">
+</parameter>
+</method>
 <method name="setRetainInstance"
  return="void"
  abstract="false"
@@ -30707,6 +30731,53 @@
 </parameter>
 </constructor>
 </class>
+<class name="Fragment.SavedState"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.ClassLoaderCreator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="FragmentBreadCrumbs"
  extends="android.view.ViewGroup"
  abstract="false"
@@ -31140,6 +31211,19 @@
 <parameter name="listener" type="android.app.FragmentManager.OnBackStackChangedListener">
 </parameter>
 </method>
+<method name="saveFragmentInstanceState"
+ return="android.app.Fragment.SavedState"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="f" type="android.app.Fragment">
+</parameter>
+</method>
 <field name="POP_BACK_STACK_INCLUSIVE"
  type="int"
  transient="false"
@@ -150847,6 +150931,31 @@
 >
 </field>
 </interface>
+<interface name="Parcelable.ClassLoaderCreator"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable.Creator">
+</implements>
+<method name="createFromParcel"
+ return="T"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="android.os.Parcel">
+</parameter>
+<parameter name="loader" type="java.lang.ClassLoader">
+</parameter>
+</method>
+</interface>
 <interface name="Parcelable.Creator"
  abstract="true"
  static="true"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 69cb27e..a61147a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1875,7 +1875,7 @@
             }
             deliverNewIntents(r, intents);
             if (resumed) {
-                mInstrumentation.callActivityOnResume(r.activity);
+                r.activity.performResume();
                 r.activity.mTemporaryPause = false;
             }
         }
@@ -2831,7 +2831,7 @@
             }
             deliverResults(r, res.results);
             if (resumed) {
-                mInstrumentation.callActivityOnResume(r.activity);
+                r.activity.performResume();
                 r.activity.mTemporaryPause = false;
             }
         }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 6f0bbd7..14ffd3b 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -450,6 +450,51 @@
     boolean mCheckedForLoaderManager;
     
     /**
+     * State information that has been retrieved from a fragment instance
+     * through {@link FragmentManager#saveFragmentInstanceState(Fragment)
+     * FragmentManager.saveFragmentInstanceState}.
+     */
+    public static class SavedState implements Parcelable {
+        final Bundle mState;
+
+        SavedState(Bundle state) {
+            mState = state;
+        }
+
+        SavedState(Parcel in, ClassLoader loader) {
+            mState = in.readBundle();
+            if (loader != null && mState != null) {
+                mState.setClassLoader(loader);
+            }
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeBundle(mState);
+        }
+
+        public static final Parcelable.ClassLoaderCreator<SavedState> CREATOR
+                = new Parcelable.ClassLoaderCreator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
+            }
+
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, loader);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    /**
      * Thrown by {@link Fragment#instantiate(Context, String, Bundle)} when
      * there is an instantiation failure.
      */
@@ -624,6 +669,22 @@
     }
 
     /**
+     * Set the initial saved state that this Fragment should restore itself
+     * from when first being constructed, as returned by
+     * {@link FragmentManager#saveFragmentInstanceState(Fragment)
+     * FragmentManager.saveFragmentInstanceState}.
+     *
+     * @param state The state the fragment should be restored from.
+     */
+    public void setInitialSavedState(SavedState state) {
+        if (mIndex >= 0) {
+            throw new IllegalStateException("Fragment already active");
+        }
+        mSavedFragmentState = state != null && state.mState != null
+                ? state.mState : null;
+    }
+
+    /**
      * Optional target for this fragment.  This may be used, for example,
      * if this fragment is being started by another, and when done wants to
      * give a result back to the first.  The target set here is retained
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 0da656f..2164ada 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -274,6 +274,30 @@
     public abstract Fragment getFragment(Bundle bundle, String key);
 
     /**
+     * Save the current instance state of the given Fragment.  This can be
+     * used later when creating a new instance of the Fragment and adding
+     * it to the fragment manager, to have it create itself to match the
+     * current state returned here.  Note that there are limits on how
+     * this can be used:
+     *
+     * <ul>
+     * <li>The Fragment must currently be attached to the FragmentManager.
+     * <li>A new Fragment created using this saved state must be the same class
+     * type as the Fragment it was created from.
+     * <li>The saved state can not contain dependencies on other fragments --
+     * that is it can't use {@link #putFragment(Bundle, String, Fragment)} to
+     * store a fragment reference because that reference may not be valid when
+     * this saved state is later used.  Likewise the Fragment's target and
+     * result code are not included in this state.
+     * </ul>
+     *
+     * @param f The Fragment whose state is to be saved.
+     * @return The generated state.  This will be null if there was no
+     * interesting state created by the fragment.
+     */
+    public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f);
+
+    /**
      * Print the FragmentManager's state into the given stream.
      *
      * @param prefix Text to print at the front of each line.
@@ -492,6 +516,19 @@
     }
 
     @Override
+    public Fragment.SavedState saveFragmentInstanceState(Fragment fragment) {
+        if (fragment.mIndex < 0) {
+            throw new IllegalStateException("Fragment " + fragment
+                    + " is not currently in the FragmentManager");
+        }
+        if (fragment.mState > Fragment.INITIALIZING) {
+            Bundle result = saveFragmentBasicState(fragment);
+            return result != null ? new Fragment.SavedState(result) : null;
+        }
+        return null;
+    }
+
+    @Override
     public String toString() {
         StringBuilder sb = new StringBuilder(128);
         sb.append("FragmentManager{");
@@ -715,7 +752,6 @@
                         if (f.mView != null) {
                             f.mView.setSaveFromParentEnabled(false);
                             if (f.mHidden) f.mView.setVisibility(View.GONE);
-                            f.restoreViewState();
                             f.onViewCreated(f.mView, f.mSavedFragmentState);
                         }
                     }
@@ -747,7 +783,6 @@
                                     container.addView(f.mView);
                                 }
                                 if (f.mHidden) f.mView.setVisibility(View.GONE);
-                                f.restoreViewState();
                                 f.onViewCreated(f.mView, f.mSavedFragmentState);
                             }
                         }
@@ -759,6 +794,7 @@
                                     + " did not call through to super.onActivityCreated()");
                         }
                         if (f.mView != null) {
+                            f.restoreViewState();
                         }
                         f.mSavedFragmentState = null;
                     }
@@ -1375,6 +1411,8 @@
         }
         if (mStateArray == null) {
             mStateArray = new SparseArray<Parcelable>();
+        } else {
+            mStateArray.clear();
         }
         f.mView.saveHierarchyState(mStateArray);
         if (mStateArray.size() > 0) {
@@ -1383,6 +1421,32 @@
         }
     }
     
+    Bundle saveFragmentBasicState(Fragment f) {
+        Bundle result = null;
+
+        if (mStateBundle == null) {
+            mStateBundle = new Bundle();
+        }
+        f.onSaveInstanceState(mStateBundle);
+        if (!mStateBundle.isEmpty()) {
+            result = mStateBundle;
+            mStateBundle = null;
+        }
+
+        if (f.mView != null) {
+            saveFragmentViewState(f);
+            if (f.mSavedViewState != null) {
+                if (result == null) {
+                    result = new Bundle();
+                }
+                result.putSparseParcelableArray(
+                        FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
+            }
+        }
+
+        return result;
+    }
+
     Parcelable saveAllState() {
         // Make sure all pending operations have now been executed to get
         // our state update-to-date.
@@ -1407,25 +1471,7 @@
                 active[i] = fs;
                 
                 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
-                    if (mStateBundle == null) {
-                        mStateBundle = new Bundle();
-                    }
-                    f.onSaveInstanceState(mStateBundle);
-                    if (!mStateBundle.isEmpty()) {
-                        fs.mSavedFragmentState = mStateBundle;
-                        mStateBundle = null;
-                    }
-
-                    if (f.mView != null) {
-                        saveFragmentViewState(f);
-                        if (f.mSavedViewState != null) {
-                            if (fs.mSavedFragmentState == null) {
-                                fs.mSavedFragmentState = new Bundle();
-                            }
-                            fs.mSavedFragmentState.putSparseParcelableArray(
-                                    FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
-                        }
-                    }
+                    fs.mSavedFragmentState = saveFragmentBasicState(f);
 
                     if (f.mTarget != null) {
                         if (f.mTarget.mIndex < 0) {
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 6b35215..e9ed676 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -1980,6 +1980,9 @@
             }
         }
 
+        if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+            return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader);
+        }
         return creator.createFromParcel(this);
     }
 
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 0a4b60f..594fbb2 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -113,4 +113,22 @@
          */
         public T[] newArray(int size);
     }
+
+    /**
+     * Specialization of {@link Creator} that allows you to receive the
+     * ClassLoader the object is being created in.
+     */
+    public interface ClassLoaderCreator<T> extends Creator<T> {
+        /**
+         * Create a new instance of the Parcelable class, instantiating it
+         * from the given Parcel whose data had previously been written by
+         * {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and
+         * using the given ClassLoader.
+         *
+         * @param source The Parcel to read the object's data from.
+         * @param loader The ClassLoader that this object is being created in.
+         * @return Returns a new instance of the Parcelable class.
+         */
+        public T createFromParcel(Parcel source, ClassLoader loader);
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fbdc0ba..58d9952 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1791,6 +1791,16 @@
         public static final String SIP_ASK_ME_EACH_TIME = "SIP_ASK_ME_EACH_TIME";
 
         /**
+         * Pointer speed setting.
+         * This is an integer value in a range between -7 and +7, so there are 15 possible values.
+         *   -7 = slowest
+         *    0 = default speed
+         *   +7 = fastest
+         * @hide
+         */
+        public static final String POINTER_SPEED = "pointer_speed";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          * @hide
@@ -1854,6 +1864,7 @@
             USE_PTP_INTERFACE,
             SIP_CALL_OPTIONS,
             SIP_RECEIVE_CALLS,
+            POINTER_SPEED,
         };
 
         // Settings moved to Settings.Secure
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 4427eb5..ad17edf 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -205,4 +205,9 @@
      * Called by the status bar to notify Views of changes to System UI visiblity.
      */
     void statusBarVisibilityChanged(int visibility);
+
+    /**
+     * Called by the settings application to temporarily set the pointer speed.
+     */
+    void setPointerSpeed(int speed);
 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a39c7c7..83d2a79 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -998,13 +998,13 @@
                 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
                         + ic + " tba=" + tba + " initial=" + initial);
                 InputBindResult res = mService.startInput(mClient,
-                        servedContext, tba, initial, mCurMethod == null);
+                        servedContext, tba, initial, true);
                 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
                 if (res != null) {
                     if (res.id != null) {
                         mBindSequence = res.sequence;
                         mCurMethod = res.method;
-                    } else {
+                    } else if (mCurMethod == null) {
                         // This means there is no input method available.
                         if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
                         return;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2f431c4..441a3bfe 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1071,6 +1071,13 @@
         android:description="@string/permdesc_setOrientation"
         android:protectionLevel="signature" />
 
+    <!-- Allows low-level access to setting the pointer speed.
+         Not for use by normal applications. -->
+    <permission android:name="android.permission.SET_POINTER_SPEED"
+        android:label="@string/permlab_setPointerSpeed"
+        android:description="@string/permdesc_setPointerSpeed"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to install packages. -->
     <permission android:name="android.permission.INSTALL_PACKAGES"
         android:label="@string/permlab_installPackages"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d5affae..b92bf88 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -699,6 +699,13 @@
         the rotation of the screen at any time. Should never be needed for
         normal applications.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+    <string name="permlab_setPointerSpeed">change pointer speed</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_setPointerSpeed">Allows an application to change
+        the mouse or trackpad pointer speed at any time. Should never be needed for
+        normal applications.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_signalPersistentProcesses">send Linux signals to applications</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/include/ui/Input.h b/include/ui/Input.h
index fb6152e..d603441 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -627,6 +627,87 @@
     int32_t mActivePointerId;
 };
 
+
+/*
+ * Specifies parameters that govern pointer or wheel acceleration.
+ */
+struct VelocityControlParameters {
+    // A scale factor that is multiplied with the raw velocity deltas
+    // prior to applying any other velocity control factors.  The scale
+    // factor should be used to adapt the input device resolution
+    // (eg. counts per inch) to the output device resolution (eg. pixels per inch).
+    //
+    // Must be a positive value.
+    // Default is 1.0 (no scaling).
+    float scale;
+
+    // The scaled speed at which acceleration begins to be applied.
+    // This value establishes the upper bound of a low speed regime for
+    // small precise motions that are performed without any acceleration.
+    //
+    // Must be a non-negative value.
+    // Default is 0.0 (no low threshold).
+    float lowThreshold;
+
+    // The scaled speed at which maximum acceleration is applied.
+    // The difference between highThreshold and lowThreshold controls
+    // the range of speeds over which the acceleration factor is interpolated.
+    // The wider the range, the smoother the acceleration.
+    //
+    // Must be a non-negative value greater than or equal to lowThreshold.
+    // Default is 0.0 (no high threshold).
+    float highThreshold;
+
+    // The acceleration factor.
+    // When the speed is above the low speed threshold, the velocity will scaled
+    // by an interpolated value between 1.0 and this amount.
+    //
+    // Must be a positive greater than or equal to 1.0.
+    // Default is 1.0 (no acceleration).
+    float acceleration;
+
+    VelocityControlParameters() :
+            scale(1.0f), lowThreshold(0.0f), highThreshold(0.0f), acceleration(1.0f) {
+    }
+
+    VelocityControlParameters(float scale, float lowThreshold,
+            float highThreshold, float acceleration) :
+            scale(scale), lowThreshold(lowThreshold),
+            highThreshold(highThreshold), acceleration(acceleration) {
+    }
+};
+
+/*
+ * Implements mouse pointer and wheel speed control and acceleration.
+ */
+class VelocityControl {
+public:
+    VelocityControl();
+
+    /* Sets the various parameters. */
+    void setParameters(const VelocityControlParameters& parameters);
+
+    /* Resets the current movement counters to zero.
+     * This has the effect of nullifying any acceleration. */
+    void reset();
+
+    /* Translates a raw movement delta into an appropriately
+     * scaled / accelerated delta based on the current velocity. */
+    void move(nsecs_t eventTime, float* deltaX, float* deltaY);
+
+private:
+    // If no movements are received within this amount of time,
+    // we assume the movement has stopped and reset the movement counters.
+    static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms
+
+    VelocityControlParameters mParameters;
+
+    nsecs_t mLastMovementTime;
+    VelocityTracker::Position mRawPosition;
+    VelocityTracker mVelocityTracker;
+};
+
+
 /*
  * Describes the characteristics and capabilities of an input device.
  */
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 684c332..50b75d5 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -13,6 +13,10 @@
 // Log debug messages about velocity tracking.
 #define DEBUG_VELOCITY 0
 
+// Log debug messages about acceleration.
+#define DEBUG_ACCELERATION 0
+
+
 #include <stdlib.h>
 #include <unistd.h>
 #include <ctype.h>
@@ -20,6 +24,7 @@
 #include <ui/Input.h>
 
 #include <math.h>
+#include <limits.h>
 
 #ifdef HAVE_ANDROID_OS
 #include <binder/Parcel.h>
@@ -670,6 +675,11 @@
 
 // --- VelocityTracker ---
 
+const uint32_t VelocityTracker::HISTORY_SIZE;
+const nsecs_t VelocityTracker::MAX_AGE;
+const nsecs_t VelocityTracker::MIN_WINDOW;
+const nsecs_t VelocityTracker::MIN_DURATION;
+
 VelocityTracker::VelocityTracker() {
     clear();
 }
@@ -879,6 +889,85 @@
 }
 
 
+// --- VelocityControl ---
+
+const nsecs_t VelocityControl::STOP_TIME;
+
+VelocityControl::VelocityControl() {
+    reset();
+}
+
+void VelocityControl::setParameters(const VelocityControlParameters& parameters) {
+    mParameters = parameters;
+    reset();
+}
+
+void VelocityControl::reset() {
+    mLastMovementTime = LLONG_MIN;
+    mRawPosition.x = 0;
+    mRawPosition.y = 0;
+    mVelocityTracker.clear();
+}
+
+void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) {
+    if ((deltaX && *deltaX) || (deltaY && *deltaY)) {
+        if (eventTime >= mLastMovementTime + STOP_TIME) {
+#if DEBUG_ACCELERATION
+            LOGD("VelocityControl: stopped, last movement was %0.3fms ago",
+                    (eventTime - mLastMovementTime) * 0.000001f);
+#endif
+            reset();
+        }
+
+        mLastMovementTime = eventTime;
+        if (deltaX) {
+            mRawPosition.x += *deltaX;
+        }
+        if (deltaY) {
+            mRawPosition.y += *deltaY;
+        }
+        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition);
+
+        float vx, vy;
+        float scale = mParameters.scale;
+        if (mVelocityTracker.getVelocity(0, &vx, &vy)) {
+            float speed = hypotf(vx, vy) * scale;
+            if (speed >= mParameters.highThreshold) {
+                // Apply full acceleration above the high speed threshold.
+                scale *= mParameters.acceleration;
+            } else if (speed > mParameters.lowThreshold) {
+                // Linearly interpolate the acceleration to apply between the low and high
+                // speed thresholds.
+                scale *= 1 + (speed - mParameters.lowThreshold)
+                        / (mParameters.highThreshold - mParameters.lowThreshold)
+                        * (mParameters.acceleration - 1);
+            }
+
+#if DEBUG_ACCELERATION
+            LOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): "
+                    "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
+                    mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
+                    mParameters.acceleration,
+                    vx, vy, speed, scale / mParameters.scale);
+#endif
+        } else {
+#if DEBUG_ACCELERATION
+            LOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity",
+                    mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
+                    mParameters.acceleration);
+#endif
+        }
+
+        if (deltaX) {
+            *deltaX *= scale;
+        }
+        if (deltaY) {
+            *deltaY *= scale;
+        }
+    }
+}
+
+
 // --- InputDeviceInfo ---
 
 InputDeviceInfo::InputDeviceInfo() {
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index bf06f947..2e2768f 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -125,4 +125,7 @@
     <!-- Default for Settings.Secure.LONG_PRESS_TIMEOUT_MILLIS -->
     <integer name="def_long_press_timeout_millis">500</integer>
 
+    <!-- Default for Settings.System.POINTER_SPEED -->
+    <integer name="def_pointer_speed">0</integer>
+
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index d901c2c..2ed968b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -1214,6 +1214,9 @@
             loadBooleanSetting(stmt, Settings.System.NOTIFICATIONS_USE_RING_VOLUME,
                     R.bool.def_notifications_use_ring_volume);
 
+            loadIntegerSetting(stmt, Settings.System.POINTER_SPEED,
+                    R.integer.def_pointer_speed);
+
         } finally {
             if (stmt != null) stmt.close();
         }
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index d80abe8..f748e7c 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -33,6 +33,7 @@
 
 #include <hardware_legacy/power.h>
 
+#include <cutils/atomic.h>
 #include <cutils/properties.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
@@ -127,6 +128,7 @@
         mError(NO_INIT), mBuiltInKeyboardId(-1), mNextDeviceId(1),
         mOpeningDevices(0), mClosingDevices(0),
         mOpened(false), mNeedToSendFinishedDeviceScan(false),
+        mNeedToReopenDevices(0), mNeedToScanDevices(false),
         mInputFdIndex(1) {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 
@@ -380,12 +382,10 @@
     return NAME_NOT_FOUND;
 }
 
-void EventHub::addExcludedDevice(const char* deviceName)
-{
+void EventHub::setExcludedDevices(const Vector<String8>& devices) {
     AutoMutex _l(mLock);
 
-    String8 name(deviceName);
-    mExcludedDevices.push_back(name);
+    mExcludedDevices = devices;
 }
 
 bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
@@ -453,9 +453,11 @@
     assert(bufferSize >= 1);
 
     if (!mOpened) {
+        android_atomic_acquire_store(0, &mNeedToReopenDevices);
+
         mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
         mOpened = true;
-        mNeedToSendFinishedDeviceScan = true;
+        mNeedToScanDevices = true;
     }
 
     struct input_event readBuffer[bufferSize];
@@ -465,6 +467,20 @@
     for (;;) {
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
 
+        // Reopen input devices if needed.
+        if (android_atomic_acquire_load(&mNeedToReopenDevices)) {
+            android_atomic_acquire_store(0, &mNeedToReopenDevices);
+
+            LOGI("Reopening all input devices due to a configuration change.");
+
+            AutoMutex _l(mLock);
+            while (mDevices.size() > 1) {
+                closeDeviceAtIndexLocked(mDevices.size() - 1);
+            }
+            mNeedToScanDevices = true;
+            break; // return to the caller before we actually rescan
+        }
+
         // Report any devices that had last been added/removed.
         while (mClosingDevices) {
             Device* device = mClosingDevices;
@@ -482,6 +498,12 @@
             }
         }
 
+        if (mNeedToScanDevices) {
+            mNeedToScanDevices = false;
+            scanDevices();
+            mNeedToSendFinishedDeviceScan = true;
+        }
+
         while (mOpeningDevices != NULL) {
             Device* device = mOpeningDevices;
             LOGV("Reporting device opened: id=%d, name=%s\n",
@@ -683,13 +705,14 @@
     pollfd.revents = 0;
     mFds.push(pollfd);
     mDevices.push(NULL);
+    return true;
+}
 
-    res = scanDir(DEVICE_PATH);
+void EventHub::scanDevices() {
+    int res = scanDir(DEVICE_PATH);
     if(res < 0) {
         LOGE("scan dir failed for %s\n", DEVICE_PATH);
     }
-
-    return true;
 }
 
 // ----------------------------------------------------------------------------
@@ -742,12 +765,10 @@
     }
 
     // Check to see if the device is on our excluded list
-    List<String8>::iterator iter = mExcludedDevices.begin();
-    List<String8>::iterator end = mExcludedDevices.end();
-    for ( ; iter != end; iter++) {
-        const char* test = *iter;
-        if (identifier.name == test) {
-            LOGI("ignoring event id %s driver %s\n", devicePath, test);
+    for (size_t i = 0; i < mExcludedDevices.size(); i++) {
+        const String8& item = mExcludedDevices.itemAt(i);
+        if (identifier.name == item) {
+            LOGI("ignoring event id %s driver %s\n", devicePath, item.string());
             close(fd);
             return -1;
         }
@@ -1210,6 +1231,10 @@
     return 0;
 }
 
+void EventHub::reopenDevices() {
+    android_atomic_release_store(1, &mNeedToReopenDevices);
+}
+
 void EventHub::dump(String8& dump) {
     dump.append("Event Hub State:\n");
 
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 4d26a95..853a0bd 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -178,9 +178,9 @@
     virtual status_t mapAxis(int32_t deviceId, int scancode,
             AxisInfo* outAxisInfo) const = 0;
 
-    // exclude a particular device from opening
-    // this can be used to ignore input devices for sensors
-    virtual void addExcludedDevice(const char* deviceName) = 0;
+    // Sets devices that are excluded from opening.
+    // This can be used to ignore input devices for sensors.
+    virtual void setExcludedDevices(const Vector<String8>& devices) = 0;
 
     /*
      * Wait for events to become available and returns them.
@@ -215,6 +215,8 @@
     virtual void getVirtualKeyDefinitions(int32_t deviceId,
             Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
 
+    virtual void reopenDevices() = 0;
+
     virtual void dump(String8& dump) = 0;
 };
 
@@ -242,7 +244,7 @@
     virtual status_t mapAxis(int32_t deviceId, int scancode,
             AxisInfo* outAxisInfo) const;
 
-    virtual void addExcludedDevice(const char* deviceName);
+    virtual void setExcludedDevices(const Vector<String8>& devices);
 
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
     virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
@@ -259,6 +261,8 @@
     virtual void getVirtualKeyDefinitions(int32_t deviceId,
             Vector<VirtualKeyDefinition>& outVirtualKeys) const;
 
+    virtual void reopenDevices();
+
     virtual void dump(String8& dump);
 
 protected:
@@ -271,6 +275,7 @@
     int closeDevice(const char *devicePath);
     int closeDeviceAtIndexLocked(int index);
     int scanDir(const char *dirname);
+    void scanDevices();
     int readNotify(int nfd);
 
     status_t mError;
@@ -333,7 +338,9 @@
 
     bool mOpened;
     bool mNeedToSendFinishedDeviceScan;
-    List<String8> mExcludedDevices;
+    volatile int32_t mNeedToReopenDevices; // must be modified atomically
+    bool mNeedToScanDevices;
+    Vector<String8> mExcludedDevices;
 
     // device ids that report particular switches.
     int32_t mSwitches[SW_MAX + 1];
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index f2b34dd..a22ec1c 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -38,6 +38,7 @@
 
 #include "InputReader.h"
 
+#include <cutils/atomic.h>
 #include <cutils/log.h>
 #include <ui/Keyboard.h>
 #include <ui/VirtualKeyMap.h>
@@ -219,10 +220,9 @@
         const sp<InputReaderPolicyInterface>& policy,
         const sp<InputDispatcherInterface>& dispatcher) :
         mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
-        mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX) {
-    mPolicy->getReaderConfiguration(&mConfig);
-
-    configureExcludedDevices();
+        mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
+        mRefreshConfiguration(0) {
+    configure(true /*firstTime*/);
     updateGlobalMetaState();
     updateInputConfiguration();
 }
@@ -234,6 +234,11 @@
 }
 
 void InputReader::loopOnce() {
+    if (android_atomic_acquire_load(&mRefreshConfiguration)) {
+        android_atomic_release_store(0, &mRefreshConfiguration);
+        configure(false /*firstTime*/);
+    }
+
     int32_t timeoutMillis = -1;
     if (mNextTimeout != LLONG_MAX) {
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -454,9 +459,12 @@
     mDispatcher->notifyConfigurationChanged(when);
 }
 
-void InputReader::configureExcludedDevices() {
-    for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) {
-        mEventHub->addExcludedDevice(mConfig.excludedDeviceNames[i]);
+void InputReader::configure(bool firstTime) {
+    mPolicy->getReaderConfiguration(&mConfig);
+    mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
+
+    if (!firstTime) {
+        mEventHub->reopenDevices();
     }
 }
 
@@ -677,6 +685,10 @@
     } // release device registy reader lock
 }
 
+void InputReader::refreshConfiguration() {
+    android_atomic_release_store(1, &mRefreshConfiguration);
+}
+
 void InputReader::dump(String8& dump) {
     mEventHub->dump(dump);
     dump.append("\n");
@@ -707,6 +719,20 @@
     dump.appendFormat(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
             mConfig.virtualKeyQuietTime * 0.000001f);
 
+    dump.appendFormat(INDENT2 "PointerVelocityControlParameters: "
+            "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
+            mConfig.pointerVelocityControlParameters.scale,
+            mConfig.pointerVelocityControlParameters.lowThreshold,
+            mConfig.pointerVelocityControlParameters.highThreshold,
+            mConfig.pointerVelocityControlParameters.acceleration);
+
+    dump.appendFormat(INDENT2 "WheelVelocityControlParameters: "
+            "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
+            mConfig.wheelVelocityControlParameters.scale,
+            mConfig.wheelVelocityControlParameters.lowThreshold,
+            mConfig.wheelVelocityControlParameters.highThreshold,
+            mConfig.wheelVelocityControlParameters.acceleration);
+
     dump.appendFormat(INDENT2 "PointerGesture:\n");
     dump.appendFormat(INDENT3 "QuietInterval: %0.1fms\n",
             mConfig.pointerGestureQuietInterval * 0.000001f);
@@ -1371,6 +1397,10 @@
 
     mHaveVWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_WHEEL);
     mHaveHWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_HWHEEL);
+
+    mPointerVelocityControl.setParameters(getConfig()->pointerVelocityControlParameters);
+    mWheelXVelocityControl.setParameters(getConfig()->wheelVelocityControlParameters);
+    mWheelYVelocityControl.setParameters(getConfig()->wheelVelocityControlParameters);
 }
 
 void CursorInputMapper::configureParameters() {
@@ -1432,6 +1462,11 @@
             }
         } // release lock
 
+        // Reset velocity.
+        mPointerVelocityControl.reset();
+        mWheelXVelocityControl.reset();
+        mWheelYVelocityControl.reset();
+
         // Synthesize button up event on reset.
         nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
         mAccumulator.clear();
@@ -1585,11 +1620,16 @@
         } else {
             vscroll = 0;
         }
+        mWheelYVelocityControl.move(when, NULL, &vscroll);
+
         if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) {
             hscroll = mAccumulator.relHWheel;
         } else {
             hscroll = 0;
         }
+        mWheelXVelocityControl.move(when, &hscroll, NULL);
+
+        mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
         if (mPointerController != NULL) {
             if (deltaX != 0 || deltaY != 0 || vscroll != 0 || hscroll != 0
@@ -1806,6 +1846,7 @@
     mLocked.orientedRanges.haveOrientation = false;
 
     mPointerGesture.reset();
+    mPointerGesture.pointerVelocityControl.setParameters(mConfig->pointerVelocityControlParameters);
 }
 
 void TouchInputMapper::configure() {
@@ -2239,11 +2280,10 @@
                     mLocked.associatedDisplayHeight);
 
             // Scale movements such that one whole swipe of the touch pad covers a
-            // given area relative to the diagonal size of the display.
+            // given area relative to the diagonal size of the display when no acceleration
+            // 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.
-            const float scaleFactor = 0.8f;
-
             mLocked.pointerGestureXMovementScale = mConfig->pointerGestureMovementSpeedRatio
                     * displayDiagonal / rawDiagonal;
             mLocked.pointerGestureYMovementScale = mLocked.pointerGestureXMovementScale;
@@ -3247,6 +3287,9 @@
     if (!sendEvents) {
         return;
     }
+    if (finishPreviousGesture) {
+        cancelPreviousGesture = false;
+    }
 
     // Switch pointer presentation.
     mPointerController->setPresentation(
@@ -3436,6 +3479,8 @@
                 mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
                 mPointerGesture.currentGestureIdBits.clear();
 
+                mPointerGesture.pointerVelocityControl.reset();
+
                 if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
                     mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
                     mPointerGesture.spotIdBits.clear();
@@ -3530,6 +3575,8 @@
         mPointerGesture.currentGestureMode = PointerGesture::QUIET;
         mPointerGesture.currentGestureIdBits.clear();
 
+        mPointerGesture.pointerVelocityControl.reset();
+
         if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
             mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
             mPointerGesture.spotIdBits.clear();
@@ -3561,46 +3608,48 @@
 
         // Switch pointers if needed.
         // Find the fastest pointer and follow it.
-        if (activeTouchId >= 0) {
-            if (mCurrentTouch.pointerCount > 1) {
-                int32_t bestId = -1;
-                float bestSpeed = mConfig->pointerGestureDragMinSwitchSpeed;
-                for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
-                    uint32_t id = mCurrentTouch.pointers[i].id;
-                    float vx, vy;
-                    if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
-                        float speed = hypotf(vx, vy);
-                        if (speed > bestSpeed) {
-                            bestId = id;
-                            bestSpeed = speed;
-                        }
+        if (activeTouchId >= 0 && mCurrentTouch.pointerCount > 1) {
+            int32_t bestId = -1;
+            float bestSpeed = mConfig->pointerGestureDragMinSwitchSpeed;
+            for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
+                uint32_t id = mCurrentTouch.pointers[i].id;
+                float vx, vy;
+                if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
+                    float speed = hypotf(vx, vy);
+                    if (speed > bestSpeed) {
+                        bestId = id;
+                        bestSpeed = speed;
                     }
                 }
-                if (bestId >= 0 && bestId != activeTouchId) {
-                    mPointerGesture.activeTouchId = activeTouchId = bestId;
-                    activeTouchChanged = true;
+            }
+            if (bestId >= 0 && bestId != activeTouchId) {
+                mPointerGesture.activeTouchId = activeTouchId = bestId;
+                activeTouchChanged = true;
 #if DEBUG_GESTURES
-                    LOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
-                            "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed);
+                LOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
+                        "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed);
 #endif
-                }
             }
+        }
 
-            if (mLastTouch.idBits.hasBit(activeTouchId)) {
-                const PointerData& currentPointer =
-                        mCurrentTouch.pointers[mCurrentTouch.idToIndex[activeTouchId]];
-                const PointerData& lastPointer =
-                        mLastTouch.pointers[mLastTouch.idToIndex[activeTouchId]];
-                float deltaX = (currentPointer.x - lastPointer.x)
-                        * mLocked.pointerGestureXMovementScale;
-                float deltaY = (currentPointer.y - lastPointer.y)
-                        * mLocked.pointerGestureYMovementScale;
+        if (activeTouchId >= 0 && mLastTouch.idBits.hasBit(activeTouchId)) {
+            const PointerData& currentPointer =
+                    mCurrentTouch.pointers[mCurrentTouch.idToIndex[activeTouchId]];
+            const PointerData& lastPointer =
+                    mLastTouch.pointers[mLastTouch.idToIndex[activeTouchId]];
+            float deltaX = (currentPointer.x - lastPointer.x)
+                    * mLocked.pointerGestureXMovementScale;
+            float deltaY = (currentPointer.y - lastPointer.y)
+                    * mLocked.pointerGestureYMovementScale;
 
-                // Move the pointer using a relative motion.
-                // When using spots, the click will occur at the position of the anchor
-                // spot and all other spots will move there.
-                mPointerController->move(deltaX, deltaY);
-            }
+            mPointerGesture.pointerVelocityControl.move(when, &deltaX, &deltaY);
+
+            // Move the pointer using a relative motion.
+            // When using spots, the click will occur at the position of the anchor
+            // spot and all other spots will move there.
+            mPointerController->move(deltaX, deltaY);
+        } else {
+            mPointerGesture.pointerVelocityControl.reset();
         }
 
         float x, y;
@@ -3700,6 +3749,8 @@
             }
         }
 
+        mPointerGesture.pointerVelocityControl.reset();
+
         if (!tapped) {
 #if DEBUG_GESTURES
             LOGD("Gestures: NEUTRAL");
@@ -3756,9 +3807,13 @@
             float deltaY = (currentPointer.y - lastPointer.y)
                     * mLocked.pointerGestureYMovementScale;
 
+            mPointerGesture.pointerVelocityControl.move(when, &deltaX, &deltaY);
+
             // Move the pointer using a relative motion.
             // When using spots, the hover or drag will occur at the position of the anchor spot.
             mPointerController->move(deltaX, deltaY);
+        } else {
+            mPointerGesture.pointerVelocityControl.reset();
         }
 
         bool down;
@@ -3820,16 +3875,32 @@
         // a decision to transition into SWIPE or FREEFORM mode accordingly.
         LOG_ASSERT(activeTouchId >= 0);
 
-        bool needReference = false;
         bool settled = when >= mPointerGesture.firstTouchTime
                 + mConfig->pointerGestureMultitouchSettleInterval;
         if (mPointerGesture.lastGestureMode != PointerGesture::PRESS
                 && mPointerGesture.lastGestureMode != PointerGesture::SWIPE
                 && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
             *outFinishPreviousGesture = true;
+        } else if (!settled && mCurrentTouch.pointerCount > mLastTouch.pointerCount) {
+            // Additional pointers have gone down but not yet settled.
+            // Reset the gesture.
+#if DEBUG_GESTURES
+            LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
+                    "settle time remaining %0.3fms",
+                    (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
+                            * 0.000001f);
+#endif
+            *outCancelPreviousGesture = true;
+        } else {
+            // Continue previous gesture.
+            mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
+        }
+
+        if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
             mPointerGesture.currentGestureMode = PointerGesture::PRESS;
             mPointerGesture.activeGestureId = 0;
             mPointerGesture.referenceIdBits.clear();
+            mPointerGesture.pointerVelocityControl.reset();
 
             if (settled && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
                     && mLastTouch.idBits.hasBit(mPointerGesture.activeTouchId)) {
@@ -3850,37 +3921,18 @@
                 mPointerGesture.referenceGestureX = c.getAxisValue(AMOTION_EVENT_AXIS_X);
                 mPointerGesture.referenceGestureY = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
             } else {
+                // Use the centroid and pointer location as the reference points for the gesture.
 #if DEBUG_GESTURES
                 LOGD("Gestures: Using centroid as reference for MULTITOUCH, "
                         "settle time remaining %0.3fms",
                         (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
                                 * 0.000001f);
 #endif
-                needReference = true;
+                mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
+                        &mPointerGesture.referenceTouchY);
+                mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+                        &mPointerGesture.referenceGestureY);
             }
-        } else if (!settled && mCurrentTouch.pointerCount > mLastTouch.pointerCount) {
-            // Additional pointers have gone down but not yet settled.
-            // Reset the gesture.
-#if DEBUG_GESTURES
-            LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
-                    "settle time remaining %0.3fms",
-                    (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
-                            * 0.000001f);
-#endif
-            *outCancelPreviousGesture = true;
-            mPointerGesture.currentGestureMode = PointerGesture::PRESS;
-            mPointerGesture.activeGestureId = 0;
-        } else {
-            // Continue previous gesture.
-            mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
-        }
-
-        if (needReference) {
-            // Use the centroid and pointer location as the reference points for the gesture.
-            mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
-                    &mPointerGesture.referenceTouchY);
-            mPointerController->getPosition(&mPointerGesture.referenceGestureX,
-                    &mPointerGesture.referenceGestureY);
         }
 
         if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
@@ -4010,10 +4062,14 @@
 
                 mPointerGesture.referenceTouchX += commonDeltaX;
                 mPointerGesture.referenceTouchY += commonDeltaY;
-                mPointerGesture.referenceGestureX +=
-                        commonDeltaX * mLocked.pointerGestureXMovementScale;
-                mPointerGesture.referenceGestureY +=
-                        commonDeltaY * mLocked.pointerGestureYMovementScale;
+
+                commonDeltaX *= mLocked.pointerGestureXMovementScale;
+                commonDeltaY *= mLocked.pointerGestureYMovementScale;
+                mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
+
+                mPointerGesture.referenceGestureX += commonDeltaX;
+                mPointerGesture.referenceGestureY += commonDeltaY;
+
                 clampPositionUsingPointerBounds(mPointerController,
                         &mPointerGesture.referenceGestureX,
                         &mPointerGesture.referenceGestureY);
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index db4679b..5028b60 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -62,6 +62,12 @@
     // Devices with these names will be ignored.
     Vector<String8> excludedDeviceNames;
 
+    // Velocity control parameters for mouse pointer movements.
+    VelocityControlParameters pointerVelocityControlParameters;
+
+    // Velocity control parameters for mouse wheel movements.
+    VelocityControlParameters wheelVelocityControlParameters;
+
     // 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.
@@ -128,6 +134,8 @@
             filterTouchEvents(false),
             filterJumpyTouchEvents(false),
             virtualKeyQuietTime(0),
+            pointerVelocityControlParameters(1.0f, 80.0f, 400.0f, 4.0f),
+            wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
             pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
             pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
             pointerGestureTapInterval(150 * 1000000LL), // 150 ms
@@ -137,7 +145,7 @@
             pointerGestureMultitouchMinSpeed(150.0f), // 150 pixels per second
             pointerGestureSwipeTransitionAngleCosine(0.5f), // cosine of 45degrees
             pointerGestureSwipeMaxWidthRatio(0.333f),
-            pointerGestureMovementSpeedRatio(0.8f),
+            pointerGestureMovementSpeedRatio(0.3f),
             pointerGestureZoomSpeedRatio(0.3f) { }
 };
 
@@ -226,6 +234,9 @@
     /* Determine whether physical keys exist for the given framework-domain key codes. */
     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;
 };
 
 
@@ -290,6 +301,8 @@
     virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
             size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
 
+    virtual void refreshConfiguration();
+
 protected:
     // These methods are protected virtual so they can be overridden and instrumented
     // by test cases.
@@ -331,18 +344,17 @@
     void timeoutExpired(nsecs_t when);
 
     void handleConfigurationChanged(nsecs_t when);
-    void configureExcludedDevices();
 
     // state management for all devices
     Mutex mStateLock;
 
-    int32_t mGlobalMetaState;
+    int32_t mGlobalMetaState; // guarded by mStateLock
     virtual void updateGlobalMetaState();
     virtual int32_t getGlobalMetaState();
 
     virtual void fadePointer();
 
-    InputConfiguration mInputConfiguration;
+    InputConfiguration mInputConfiguration; // guarded by mStateLock
     void updateInputConfiguration();
 
     nsecs_t mDisableVirtualKeysTimeout; // only accessed by reader thread
@@ -350,9 +362,12 @@
     virtual bool shouldDropVirtualKey(nsecs_t now,
             InputDevice* device, int32_t keyCode, int32_t scanCode);
 
-    nsecs_t mNextTimeout; // only accessed by reader thread
+    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);
+
     // state queries
     typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
     int32_t getState(int32_t deviceId, uint32_t sourceMask, int32_t code,
@@ -629,6 +644,12 @@
     float mVWheelScale;
     float mHWheelScale;
 
+    // Velocity controls for mouse pointer and wheel movements.
+    // The controls for X and Y wheel movements are separate to keep them decoupled.
+    VelocityControl mPointerVelocityControl;
+    VelocityControl mWheelXVelocityControl;
+    VelocityControl mWheelYVelocityControl;
+
     sp<PointerControllerInterface> mPointerController;
 
     struct LockedState {
@@ -1133,6 +1154,9 @@
         // A velocity tracker for determining whether to switch active pointers during drags.
         VelocityTracker velocityTracker;
 
+        // Velocity control for pointer movements.
+        VelocityControl pointerVelocityControl;
+
         void reset() {
             firstTouchTime = LLONG_MIN;
             activeTouchId = -1;
@@ -1147,6 +1171,7 @@
             velocityTracker.clear();
             resetTap();
             resetQuietTime();
+            pointerVelocityControl.reset();
         }
 
         void resetTap() {
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index b5f603a..e85ae1f 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -617,8 +617,8 @@
         return NAME_NOT_FOUND;
     }
 
-    virtual void addExcludedDevice(const char* deviceName) {
-        mExcludedDevices.add(String8(deviceName));
+    virtual void setExcludedDevices(const Vector<String8>& devices) {
+        mExcludedDevices = devices;
     }
 
     virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
@@ -716,6 +716,9 @@
 
     virtual void dump(String8& dump) {
     }
+
+    virtual void reopenDevices() {
+    }
 };
 
 
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index ab781f4..4eda684 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -23,10 +23,14 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
+import android.database.ContentObserver;
 import android.os.Environment;
+import android.os.Handler;
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.os.SystemProperties;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.util.Slog;
 import android.util.Xml;
 import android.view.InputChannel;
@@ -56,7 +60,7 @@
     private final Callbacks mCallbacks;
     private final Context mContext;
     private final WindowManagerService mWindowManagerService;
-    
+
     private static native void nativeInit(Context context,
             Callbacks callbacks, MessageQueue messageQueue);
     private static native void nativeStart();
@@ -85,6 +89,7 @@
     private static native int[] nativeGetInputDeviceIds();
     private static native boolean nativeTransferTouchFocus(InputChannel fromChannel,
             InputChannel toChannel);
+    private static native void nativeSetPointerSpeed(int speed);
     private static native String nativeDump();
     
     // Input event injection constants defined in InputDispatcher.h.
@@ -123,10 +128,13 @@
         Slog.i(TAG, "Initializing input manager");
         nativeInit(mContext, mCallbacks, looper.getQueue());
     }
-    
+
     public void start() {
         Slog.i(TAG, "Starting input manager");
         nativeStart();
+
+        registerPointerSpeedSettingObserver();
+        updatePointerSpeedFromSettings();
     }
     
     public void setDisplaySize(int displayId, int width, int height) {
@@ -359,6 +367,42 @@
         return nativeTransferTouchFocus(fromChannel, toChannel);
     }
 
+    /**
+     * Set the pointer speed.
+     * @param speed The pointer speed as a value between -7 (slowest) and 7 (fastest)
+     * where 0 is the default speed.
+     */
+    public void setPointerSpeed(int speed) {
+        speed = Math.min(Math.max(speed, -7), 7);
+        nativeSetPointerSpeed(speed);
+    }
+
+    public void updatePointerSpeedFromSettings() {
+        int speed = getPointerSpeedSetting(0);
+        setPointerSpeed(speed);
+    }
+
+    private void registerPointerSpeedSettingObserver() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(Settings.System.POINTER_SPEED), true,
+                new ContentObserver(mWindowManagerService.mH) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        updatePointerSpeedFromSettings();
+                    }
+                });
+    }
+
+    private int getPointerSpeedSetting(int defaultValue) {
+        int speed = defaultValue;
+        try {
+            speed = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.POINTER_SPEED);
+        } catch (SettingNotFoundException snfe) {
+        }
+        return speed;
+    }
+
     public void dump(PrintWriter pw) {
         String dumpStr = nativeDump();
         if (dumpStr != null) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 7760897..4ff6b06 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5947,6 +5947,19 @@
         }
     }
 
+    /**
+     * Temporarily set the pointer speed.  Does not save the new setting.
+     * Used by the settings application.
+     */
+    public void setPointerSpeed(int speed) {
+        if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED,
+                "setPointerSpeed()")) {
+            throw new SecurityException("Requires SET_POINTER_SPEED permission");
+        }
+
+        mInputManager.setPointerSpeed(speed);
+    }
+
     private WindowState getFocusedWindow() {
         synchronized (mWindowMap) {
             return getFocusedWindowLocked();
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index fef41c9..2704647 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -53,6 +53,11 @@
 
 namespace android {
 
+// The exponent used to calculate the pointer speed scaling factor.
+// The scaling factor is calculated as 2 ^ (speed * exponent),
+// where the speed ranges from -7 to + 7 and is supplied by the user.
+static const float POINTER_SPEED_EXPONENT = 1.0f / 3;
+
 static struct {
     jclass clazz;
 
@@ -179,6 +184,7 @@
     void setFocusedApplication(JNIEnv* env, jobject applicationObj);
     void setInputDispatchMode(bool enabled, bool frozen);
     void setSystemUiVisibility(int32_t visibility);
+    void setPointerSpeed(int32_t speed);
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -227,6 +233,9 @@
         // System UI visibility.
         int32_t systemUiVisibility;
 
+        // Pointer speed.
+        int32_t pointerSpeed;
+
         // Sprite controller singleton, created on first use.
         sp<SpriteController> spriteController;
 
@@ -266,6 +275,7 @@
         mLocked.displayOrientation = ROTATION_0;
 
         mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
+        mLocked.pointerSpeed = 0;
     }
 
     sp<EventHub> eventHub = new EventHub();
@@ -429,6 +439,13 @@
     if (!checkAndClearExceptionFromCallback(env, "getTouchSlop")) {
         outConfig->pointerGestureTapSlop = touchSlop;
     }
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed
+                * POINTER_SPEED_EXPONENT);
+    } // release lock
 }
 
 sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t deviceId) {
@@ -634,6 +651,17 @@
             : PointerController::INACTIVITY_TIMEOUT_NORMAL);
 }
 
+void NativeInputManager::setPointerSpeed(int32_t speed) {
+    AutoMutex _l(mLock);
+
+    if (mLocked.pointerSpeed != speed) {
+        LOGI("Setting pointer speed to %d.", speed);
+        mLocked.pointerSpeed = speed;
+
+        mInputManager->getReader()->refreshConfiguration();
+    }
+}
+
 bool NativeInputManager::isScreenOn() {
     return android_server_PowerManagerService_isScreenOn();
 }
@@ -1180,6 +1208,15 @@
             transferTouchFocus(fromChannel, toChannel);
 }
 
+static void android_server_InputManager_nativeSetPointerSpeed(JNIEnv* env,
+        jclass clazz, jint speed) {
+    if (checkInputManagerUnitialized(env)) {
+        return;
+    }
+
+    gNativeInputManager->setPointerSpeed(speed);
+}
+
 static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) {
     if (checkInputManagerUnitialized(env)) {
         return NULL;
@@ -1234,6 +1271,8 @@
             (void*) android_server_InputManager_nativeGetInputConfiguration },
     { "nativeTransferTouchFocus", "(Landroid/view/InputChannel;Landroid/view/InputChannel;)Z",
             (void*) android_server_InputManager_nativeTransferTouchFocus },
+    { "nativeSetPointerSpeed", "(I)V",
+            (void*) android_server_InputManager_nativeSetPointerSpeed },
     { "nativeDump", "()Ljava/lang/String;",
             (void*) android_server_InputManager_nativeDump },
 };