Merge "Fix bug 4501658 - adjust toast frame padding" into honeycomb-mr2
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d30010a..063665b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -172,6 +172,11 @@
     // These can be accessed by multiple threads; mPackages is the lock.
     // XXX For now we keep around information about all packages we have
     // seen, not removing entries from this map.
+    // NOTE: The activity manager in its process needs to call in to
+    // ActivityThread to do things like update resource configurations,
+    // which means this lock gets held while the activity manager holds its
+    // own lock.  Thus you MUST NEVER call back into the activity manager
+    // or anything that depends on it while holding this lock.
     final HashMap<String, WeakReference<LoadedApk>> mPackages
             = new HashMap<String, WeakReference<LoadedApk>>();
     final HashMap<String, WeakReference<LoadedApk>> mResourcePackages
@@ -1480,7 +1485,7 @@
     }
 
     public Configuration getConfiguration() {
-        return mConfiguration;
+        return mResConfiguration;
     }
 
     public boolean isProfiling() {
@@ -1525,7 +1530,7 @@
         synchronized (this) {
             ContextImpl context = getSystemContext();
             context.init(new LoadedApk(this, "android", context, info,
-                    new CompatibilityInfo(info, 0, 0, false)), null, this);
+                    CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO), null, this);
         }
     }
 
@@ -3274,6 +3279,12 @@
         }
     }
 
+    public final void applyConfigurationToResources(Configuration config) {
+        synchronized (mPackages) {
+            applyConfigurationToResourcesLocked(config, null);
+        }
+    }
+
     final boolean applyConfigurationToResourcesLocked(Configuration config,
             CompatibilityInfo compat) {
         if (mResConfiguration == null) {
@@ -3492,8 +3503,7 @@
          * reflect configuration changes. The configuration object passed
          * in AppBindData can be safely assumed to be up to date
          */
-        Resources.getSystem().updateConfiguration(mConfiguration,
-                Resources.getSystem().getDisplayMetrics(), data.compatInfo);
+        applyConfigurationToResourcesLocked(data.config, data.compatInfo);
 
         data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
 
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 5307696..6287d33 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -98,6 +98,12 @@
         return mApplication;
     }
 
+    /**
+     * Create information about a new .apk
+     *
+     * NOTE: This constructor is called with ActivityThread's lock held,
+     * so MUST NOT call back out to the activity manager.
+     */
     public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
             CompatibilityInfo compatInfo,
             ActivityThread mainThread, ClassLoader baseLoader,
diff --git a/core/java/android/content/res/CompatibilityInfo.aidl b/core/java/android/content/res/CompatibilityInfo.aidl
new file mode 100644
index 0000000..cde3d7b
--- /dev/null
+++ b/core/java/android/content/res/CompatibilityInfo.aidl
@@ -0,0 +1,20 @@
+/*
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+package android.content.res;
+
+parcelable CompatibilityInfo;
+
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index e42caca..b7b6de6 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -243,11 +243,11 @@
     }
     
     public boolean neverSupportsScreen() {
-        return (mCompatibilityFlags&NEVER_NEEDS_COMPAT) != 0;
+        return (mCompatibilityFlags&ALWAYS_NEEDS_COMPAT) != 0;
     }
 
     public boolean alwaysSupportsScreen() {
-        return (mCompatibilityFlags&ALWAYS_NEEDS_COMPAT) != 0;
+        return (mCompatibilityFlags&NEVER_NEEDS_COMPAT) != 0;
     }
 
     /**
@@ -406,7 +406,7 @@
         if (!supportsScreen()) {
             // This is a larger screen device and the app is not
             // compatible with large screens, so diddle it.
-            CompatibilityInfo.updateCompatibleScreenFrame(inoutDm, null, inoutDm);
+            CompatibilityInfo.computeCompatibleScaling(inoutDm, inoutDm);
         } else {
             inoutDm.widthPixels = inoutDm.unscaledWidthPixels;
             inoutDm.heightPixels = inoutDm.unscaledHeightPixels;
@@ -443,8 +443,7 @@
      * @param outRect the output parameter which will contain the result.
      * @return Returns the scaling factor for the window.
      */
-    public static float updateCompatibleScreenFrame(DisplayMetrics dm,
-            Rect outRect, DisplayMetrics outDm) {
+    public static float computeCompatibleScaling(DisplayMetrics dm, DisplayMetrics outDm) {
         final int width = dm.unscaledWidthPixels;
         final int height = dm.unscaledHeightPixels;
         int shortSize, longSize;
@@ -477,12 +476,6 @@
             scale = 1;
         }
 
-        if (outRect != null) {
-            final int left = (int)((width-(newWidth*scale))/2);
-            final int top = (int)((height-(newHeight*scale))/2);
-            outRect.set(left, top, left+newWidth, top+newHeight);
-        }
-
         if (outDm != null) {
             outDm.widthPixels = newWidth;
             outDm.heightPixels = newHeight;
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index d476997..6409aac 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -330,17 +330,17 @@
         if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
             sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
         } else {
-            sb.append("?swdp");
+            sb.append(" ?swdp");
         }
         if (screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
             sb.append(" w"); sb.append(screenWidthDp); sb.append("dp");
         } else {
-            sb.append("?wdp");
+            sb.append(" ?wdp");
         }
         if (screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
             sb.append(" h"); sb.append(screenHeightDp); sb.append("dp");
         } else {
-            sb.append("?hdp");
+            sb.append(" ?hdp");
         }
         switch ((screenLayout&SCREENLAYOUT_SIZE_MASK)) {
             case SCREENLAYOUT_SIZE_UNDEFINED: sb.append(" ?lsize"); break;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 81dc46a..70bf524 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -28,14 +28,13 @@
 import android.graphics.drawable.Drawable.ConstantState;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedValue;
 import android.util.LongSparseArray;
-import android.view.Display;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -1406,6 +1405,12 @@
     public void updateConfiguration(Configuration config,
             DisplayMetrics metrics, CompatibilityInfo compat) {
         synchronized (mTmpValue) {
+            if (false) {
+                Slog.i(TAG, "**** Updating config of " + this + ": old config is "
+                        + mConfiguration + " old compat is " + mCompatibilityInfo);
+                Slog.i(TAG, "**** Updating config of " + this + ": new config is "
+                        + config + " new compat is " + compat);
+            }
             if (compat != null) {
                 mCompatibilityInfo = compat;
             }
@@ -1471,6 +1476,11 @@
                     mConfiguration.screenLayout, mConfiguration.uiMode,
                     Build.VERSION.RESOURCES_SDK_INT);
 
+            if (false) {
+                Slog.i(TAG, "**** Updating config of " + this + ": final config is " + mConfiguration
+                        + " final compat is " + mCompatibilityInfo);
+            }
+
             clearDrawableCache(mDrawableCache, configChanges);
             clearDrawableCache(mColorDrawableCache, configChanges);
 
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index bdf04ab0..4427eb5 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -19,6 +19,7 @@
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Point;
@@ -88,7 +89,7 @@
     void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim);
     void executeAppTransition();
     void setAppStartingWindow(IBinder token, String pkg, int theme,
-            CharSequence nonLocalizedLabel, int labelRes,
+            in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
             int icon, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
     void setAppWillBeHidden(IBinder token);
     void setAppVisibility(IBinder token, boolean visible);
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index beb23aa..4d4569c1 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.content.Context;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -537,7 +538,7 @@
      * @see #removeStartingWindow
      */
     public View addStartingWindow(IBinder appToken, String packageName,
-            int theme, CharSequence nonLocalizedLabel,
+            int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel,
             int labelRes, int icon, int windowFlags);
 
     /**
diff --git a/docs/html/guide/developing/debugging/debugging-ui.jd b/docs/html/guide/developing/debugging/debugging-ui.jd
index d2c42d5..22748be 100644
--- a/docs/html/guide/developing/debugging/debugging-ui.jd
+++ b/docs/html/guide/developing/debugging/debugging-ui.jd
@@ -31,10 +31,29 @@
         </li>
         <li><a href="#layoutopt">Optimizing Layouts with <code>layoutopt</code></a></li>
       </ol>
+      <h2>Related videos</h2>
+          <ol>
+              <li>
+<iframe title="Hierarchyviewer" 
+    width="272" height="234" 
+    src="http://www.youtube.com/embed/PAgE7saQUUY?rel=0&amp;hd=1" 
+    frameborder="0" allowfullscreen>
+</iframe>
+              </li>
+              <li>
+<iframe title="Pixel Perfect" 
+    width="272" height="234" 
+    src="http://www.youtube.com/embed/C45bMZGdN7Y?rel=0&amp;hd=1" 
+    frameborder="0" 
+    allowfullscreen>
+</iframe>
+              </li>
+          </ol>
     </div>
   </div>
 
-  <p>Sometimes your application's layout can slow down your application.
+  <p>
+Sometimes your application's layout can slow down your application.
   To help debug issues in your layout, the Android SDK provides the Hierarchy Viewer and
   <code>layoutopt</code> tools.
   </p>
diff --git a/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd b/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd
index bd542bd..cfed742 100644
--- a/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd
+++ b/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd
@@ -10,8 +10,8 @@
   <li>All activities belong to a task</li>
   <li>A task contains a collection of activities in the order in which the user interacts with
 them</li>
-  <li>Tasks can move to the background and retain the state of each activity in order for the user
-to perform other tasks without loosing their work</li>
+  <li>Tasks can move to the background and retain the state of each activity in order for users
+to perform other tasks without losing their work</li>
 </ul>
 
 <h2>In this document</h2>
@@ -301,7 +301,7 @@
 and D on top (the stack is A-B-C-D; D is on top). An intent arrives for an activity of type D.
 If D has the default {@code "standard"} launch mode, a new instance of the class is launched and the
 stack becomes A-B-C-D-D. However, if D's launch mode is {@code "singleTop"}, the existing instance
-of D is deliverd the intent through {@link
+of D receives the intent through {@link
 android.app.Activity#onNewIntent onNewIntent()}, because it's at the top of the stack&mdash;the
 stack remains A-B-C-D. However, if an intent arrives for an activity of type B, then a new
 instance of B is added to the stack, even if its launch mode is {@code "singleTop"}.</p>
@@ -358,7 +358,7 @@
 
 <p class="note"><strong>Note:</strong> The behaviors that you specify for your activity with the <a
 href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> attribute
-can be overriden by flags included with the intent that start your activity, as discussed in the
+can be overridden by flags included with the intent that start your activity, as discussed in the
 next section.</p>
 
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 60066e0d..36201ac 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -29,6 +29,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -1110,9 +1111,9 @@
     }
     
     /** {@inheritDoc} */
-    public View addStartingWindow(IBinder appToken, String packageName,
-                                  int theme, CharSequence nonLocalizedLabel,
-                                  int labelRes, int icon, int windowFlags) {
+    public View addStartingWindow(IBinder appToken, String packageName, int theme,
+            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
+            int icon, int windowFlags) {
         if (!SHOW_STARTING_ANIMATIONS) {
             return null;
         }
@@ -1158,8 +1159,12 @@
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
                 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
     
+            if (!compatInfo.supportsScreen()) {
+                win.addFlags(WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW);
+            }
+
             win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
-                                WindowManager.LayoutParams.MATCH_PARENT);
+                    WindowManager.LayoutParams.MATCH_PARENT);
     
             final WindowManager.LayoutParams params = win.getAttributes();
             params.token = appToken;
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 9a77af3..5ea12c7 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -222,13 +222,14 @@
 
     mKeyRepeatState.lastKeyEntry = NULL;
 
-    int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond();
-    mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond;
+    policy->getDispatcherConfiguration(&mConfig);
+
+    mThrottleState.minTimeBetweenEvents = 1000000000LL / mConfig.maxEventsPerSecond;
     mThrottleState.lastDeviceId = -1;
 
 #if DEBUG_THROTTLING
     mThrottleState.originalSampleCount = 0;
-    LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
+    LOGD("Throttling - Max events per second = %d", mConfig.maxEventsPerSecond);
 #endif
 }
 
@@ -247,13 +248,10 @@
 }
 
 void InputDispatcher::dispatchOnce() {
-    nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
-    nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
-
     nsecs_t nextWakeupTime = LONG_LONG_MAX;
     { // acquire lock
         AutoMutex _l(mLock);
-        dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
+        dispatchOnceInnerLocked(&nextWakeupTime);
 
         if (runCommandsLockedInterruptible()) {
             nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
@@ -266,14 +264,13 @@
     mLooper->pollOnce(timeoutMillis);
 }
 
-void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
-        nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {
+void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
     nsecs_t currentTime = now();
 
     // Reset the key repeat timer whenever we disallow key events, even if the next event
     // is not a key.  This is to ensure that we abort a key repeat if the device is just coming
     // out of sleep.
-    if (keyRepeatTimeout < 0) {
+    if (!mPolicy->isKeyRepeatEnabled()) {
         resetKeyRepeatLocked();
     }
 
@@ -307,7 +304,7 @@
             // Synthesize a key repeat if appropriate.
             if (mKeyRepeatState.lastKeyEntry) {
                 if (currentTime >= mKeyRepeatState.nextRepeatTime) {
-                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime, keyRepeatDelay);
+                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
                 } else {
                     if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
                         *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
@@ -426,8 +423,7 @@
         if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
             dropReason = DROP_REASON_BLOCKED;
         }
-        done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,
-                &dropReason, nextWakeupTime);
+        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
         break;
     }
 
@@ -686,8 +682,7 @@
     }
 }
 
-InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(
-        nsecs_t currentTime, nsecs_t keyRepeatDelay) {
+InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
     KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
 
     // Reuse the repeated key entry if it is otherwise unreferenced.
@@ -715,7 +710,7 @@
     // mKeyRepeatState.lastKeyEntry in addition to the one we return.
     entry->refCount += 1;
 
-    mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay;
+    mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay;
     return entry;
 }
 
@@ -735,8 +730,7 @@
     return true;
 }
 
-bool InputDispatcher::dispatchKeyLocked(
-        nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
+bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
         DropReason* dropReason, nsecs_t* nextWakeupTime) {
     // Preprocessing.
     if (! entry->dispatchInProgress) {
@@ -756,7 +750,7 @@
             } else {
                 // Not a repeat.  Save key down state in case we do see a repeat later.
                 resetKeyRepeatLocked();
-                mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
+                mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
             }
             mKeyRepeatState.lastKeyEntry = entry;
             entry->refCount += 1;
@@ -3510,6 +3504,11 @@
 void InputDispatcher::dump(String8& dump) {
     dump.append("Input Dispatcher State:\n");
     dumpDispatchStateLocked(dump);
+
+    dump.append(INDENT "Configuration:\n");
+    dump.appendFormat(INDENT2 "MaxEventsPerSecond: %d\n", mConfig.maxEventsPerSecond);
+    dump.appendFormat(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f);
+    dump.appendFormat(INDENT2 "KeyRepeatTimeout: %0.1fms\n", mConfig.keyRepeatTimeout * 0.000001f);
 }
 
 
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 39fa203..5631ef9 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -123,6 +123,30 @@
 
 
 /*
+ * Input dispatcher configuration.
+ *
+ * Specifies various options that modify the behavior of the input dispatcher.
+ */
+struct InputDispatcherConfiguration {
+    // The key repeat initial timeout.
+    nsecs_t keyRepeatTimeout;
+
+    // The key repeat inter-key delay.
+    nsecs_t keyRepeatDelay;
+
+    // The maximum suggested event delivery rate per second.
+    // This value is used to throttle motion event movement actions on a per-device
+    // basis.  It is not intended to be a hard limit.
+    int32_t maxEventsPerSecond;
+
+    InputDispatcherConfiguration() :
+            keyRepeatTimeout(500 * 1000000LL),
+            keyRepeatDelay(50 * 1000000LL),
+            maxEventsPerSecond(60) { }
+};
+
+
+/*
  * Input dispatcher policy interface.
  *
  * The input reader policy is used by the input reader to interact with the Window Manager
@@ -148,17 +172,11 @@
     /* Notifies the system that an input channel is unrecoverably broken. */
     virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle) = 0;
 
-    /* Gets the key repeat initial timeout or -1 if automatic key repeating is disabled. */
-    virtual nsecs_t getKeyRepeatTimeout() = 0;
+    /* Gets the input dispatcher configuration. */
+    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
 
-    /* Gets the key repeat inter-key delay. */
-    virtual nsecs_t getKeyRepeatDelay() = 0;
-
-    /* Gets the maximum suggested event delivery rate per second.
-     * This value is used to throttle motion event movement actions on a per-device
-     * basis.  It is not intended to be a hard limit.
-     */
-    virtual int32_t getMaxEventsPerSecond() = 0;
+    /* Returns true if automatic key repeating is enabled. */
+    virtual bool isKeyRepeatEnabled() = 0;
 
     /* Intercepts a key event immediately before queueing it.
      * The policy can use this method as an opportunity to perform power management functions
@@ -757,6 +775,7 @@
     };
 
     sp<InputDispatcherPolicyInterface> mPolicy;
+    InputDispatcherConfiguration mConfig;
 
     Mutex mLock;
 
@@ -769,8 +788,7 @@
 
     Vector<EventEntry*> mTempCancelationEvents;
 
-    void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay,
-            nsecs_t* nextWakeupTime);
+    void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime);
 
     // Batches a new sample onto a motion entry.
     // Assumes that the we have already checked that we can append samples.
@@ -842,7 +860,7 @@
     } mKeyRepeatState;
 
     void resetKeyRepeatLocked();
-    KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime, nsecs_t keyRepeatTimeout);
+    KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime);
 
     // Deferred command processing.
     bool runCommandsLockedInterruptible();
@@ -899,7 +917,7 @@
     bool dispatchConfigurationChangedLocked(
             nsecs_t currentTime, ConfigurationChangedEntry* entry);
     bool dispatchKeyLocked(
-            nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
+            nsecs_t currentTime, KeyEntry* entry,
             DropReason* dropReason, nsecs_t* nextWakeupTime);
     bool dispatchMotionLocked(
             nsecs_t currentTime, MotionEntry* entry,
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index cb69008..f2b34dd 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -56,67 +56,6 @@
 
 namespace android {
 
-// --- Constants ---
-
-// Quiet time between certain gesture transitions.
-// Time to allow for all fingers or buttons to settle into a stable state before
-// starting a new gesture.
-static const nsecs_t QUIET_INTERVAL = 100 * 1000000; // 100 ms
-
-// The minimum speed that a pointer must travel for us to consider switching the active
-// touch pointer to it during a drag.  This threshold is set to avoid switching due
-// to noise from a finger resting on the touch pad (perhaps just pressing it down).
-static const float DRAG_MIN_SWITCH_SPEED = 50.0f; // pixels per second
-
-// Tap gesture delay time.
-// The time between down and up must be less than this to be considered a tap.
-static const nsecs_t TAP_INTERVAL = 150 * 1000000; // 150 ms
-
-// Tap drag gesture delay time.
-// The time between up and the next up must be greater than this to be considered a
-// drag.  Otherwise, the previous tap is finished and a new tap begins.
-static const nsecs_t TAP_DRAG_INTERVAL = 150 * 1000000; // 150 ms
-
-// The distance in pixels that the pointer is allowed to move from initial down
-// to up and still be called a tap.
-static const float TAP_SLOP = 10.0f; // 10 pixels
-
-// Time after the first touch points go down to settle on an initial centroid.
-// This is intended to be enough time to handle cases where the user puts down two
-// fingers at almost but not quite exactly the same time.
-static const nsecs_t MULTITOUCH_SETTLE_INTERVAL = 100 * 1000000; // 100ms
-
-// The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
-// both of the pointers are moving at least this fast.
-static const float MULTITOUCH_MIN_SPEED = 150.0f; // pixels per second
-
-// The transition from PRESS to SWIPE gesture mode can only occur when the
-// cosine of the angle between the two vectors is greater than or equal to than this value
-// which indicates that the vectors are oriented in the same direction.
-// When the vectors are oriented in the exactly same direction, the cosine is 1.0.
-// (In exactly opposite directions, the cosine is -1.0.)
-static const float SWIPE_TRANSITION_ANGLE_COSINE = 0.5f; // cosine of 45 degrees
-
-// The transition from PRESS to SWIPE gesture mode can only occur when the
-// fingers are no more than this far apart relative to the diagonal size of
-// the touch pad.  For example, a ratio of 0.5 means that the fingers must be
-// no more than half the diagonal size of the touch pad apart.
-static const float SWIPE_MAX_WIDTH_RATIO = 0.333f; // 1/3
-
-// The gesture movement speed factor relative to the size of the display.
-// Movement speed applies when the fingers are moving in the same direction.
-// Without acceleration, a full swipe of the touch pad diagonal in movement mode
-// will cover this portion of the display diagonal.
-static const float GESTURE_MOVEMENT_SPEED_RATIO = 0.8f;
-
-// The gesture zoom speed factor relative to the size of the display.
-// Zoom speed applies when the fingers are mostly moving relative to each other
-// to execute a scale gesture or similar.
-// Without acceleration, a full swipe of the touch pad diagonal in zoom mode
-// will cover this portion of the display diagonal.
-static const float GESTURE_ZOOM_SPEED_RATIO = 0.3f;
-
-
 // --- Static Functions ---
 
 template<typename T>
@@ -281,6 +220,8 @@
         const sp<InputDispatcherInterface>& dispatcher) :
         mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
         mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX) {
+    mPolicy->getReaderConfiguration(&mConfig);
+
     configureExcludedDevices();
     updateGlobalMetaState();
     updateInputConfiguration();
@@ -514,11 +455,8 @@
 }
 
 void InputReader::configureExcludedDevices() {
-    Vector<String8> excludedDeviceNames;
-    mPolicy->getExcludedDeviceNames(excludedDeviceNames);
-
-    for (size_t i = 0; i < excludedDeviceNames.size(); i++) {
-        mEventHub->addExcludedDevice(excludedDeviceNames[i]);
+    for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) {
+        mEventHub->addExcludedDevice(mConfig.excludedDeviceNames[i]);
     }
 }
 
@@ -752,6 +690,46 @@
             mDevices.valueAt(i)->dump(dump);
         }
     } // release device registy reader lock
+
+    dump.append(INDENT "Configuration:\n");
+    dump.append(INDENT2 "ExcludedDeviceNames: [");
+    for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) {
+        if (i != 0) {
+            dump.append(", ");
+        }
+        dump.append(mConfig.excludedDeviceNames.itemAt(i).string());
+    }
+    dump.append("]\n");
+    dump.appendFormat(INDENT2 "FilterTouchEvents: %s\n",
+            toString(mConfig.filterTouchEvents));
+    dump.appendFormat(INDENT2 "FilterJumpyTouchEvents: %s\n",
+            toString(mConfig.filterJumpyTouchEvents));
+    dump.appendFormat(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
+            mConfig.virtualKeyQuietTime * 0.000001f);
+
+    dump.appendFormat(INDENT2 "PointerGesture:\n");
+    dump.appendFormat(INDENT3 "QuietInterval: %0.1fms\n",
+            mConfig.pointerGestureQuietInterval * 0.000001f);
+    dump.appendFormat(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n",
+            mConfig.pointerGestureDragMinSwitchSpeed);
+    dump.appendFormat(INDENT3 "TapInterval: %0.1fms\n",
+            mConfig.pointerGestureTapInterval * 0.000001f);
+    dump.appendFormat(INDENT3 "TapDragInterval: %0.1fms\n",
+            mConfig.pointerGestureTapDragInterval * 0.000001f);
+    dump.appendFormat(INDENT3 "TapSlop: %0.1fpx\n",
+            mConfig.pointerGestureTapSlop);
+    dump.appendFormat(INDENT3 "MultitouchSettleInterval: %0.1fms\n",
+            mConfig.pointerGestureMultitouchSettleInterval * 0.000001f);
+    dump.appendFormat(INDENT3 "MultitouchMinSpeed: %0.1fpx/s\n",
+            mConfig.pointerGestureMultitouchMinSpeed);
+    dump.appendFormat(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n",
+            mConfig.pointerGestureSwipeTransitionAngleCosine);
+    dump.appendFormat(INDENT3 "SwipeMaxWidthRatio: %0.1f\n",
+            mConfig.pointerGestureSwipeMaxWidthRatio);
+    dump.appendFormat(INDENT3 "MovementSpeedRatio: %0.1f\n",
+            mConfig.pointerGestureMovementSpeedRatio);
+    dump.appendFormat(INDENT3 "ZoomSpeedRatio: %0.1f\n",
+            mConfig.pointerGestureZoomSpeedRatio);
 }
 
 
@@ -1696,6 +1674,8 @@
 
 TouchInputMapper::TouchInputMapper(InputDevice* device) :
         InputMapper(device) {
+    mConfig = getConfig();
+
     mLocked.surfaceOrientation = -1;
     mLocked.surfaceWidth = -1;
     mLocked.surfaceHeight = -1;
@@ -1868,10 +1848,9 @@
 }
 
 void TouchInputMapper::configureParameters() {
-    mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents();
-    mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents();
-    mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
-    mParameters.virtualKeyQuietTime = getPolicy()->getVirtualKeyQuietTime();
+    mParameters.useBadTouchFilter = mConfig->filterTouchEvents;
+    mParameters.useAveragingTouchFilter = mConfig->filterTouchEvents;
+    mParameters.useJumpyTouchFilter = mConfig->filterJumpyTouchEvents;
 
     // TODO: select the default gesture mode based on whether the device supports
     // distinct multitouch
@@ -2265,21 +2244,22 @@
             // X and Y of the same number of raw units cover the same physical distance.
             const float scaleFactor = 0.8f;
 
-            mLocked.pointerGestureXMovementScale = GESTURE_MOVEMENT_SPEED_RATIO
+            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 = GESTURE_ZOOM_SPEED_RATIO
+            mLocked.pointerGestureXZoomScale = mConfig->pointerGestureZoomSpeedRatio
                     * displayDiagonal / rawDiagonal;
             mLocked.pointerGestureYZoomScale = mLocked.pointerGestureXZoomScale;
 
             // Max width between pointers to detect a swipe gesture is more than some fraction
             // of the diagonal axis of the touch pad.  Touches that are wider than this are
             // translated into freeform gestures.
-            mLocked.pointerGestureMaxSwipeWidth = SWIPE_MAX_WIDTH_RATIO * rawDiagonal;
+            mLocked.pointerGestureMaxSwipeWidth =
+                    mConfig->pointerGestureSwipeMaxWidthRatio * rawDiagonal;
 
             // Reset the current pointer gesture.
             mPointerGesture.reset();
@@ -2945,8 +2925,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 (mParameters.virtualKeyQuietTime > 0 && mCurrentTouch.pointerCount != 0) {
-        mContext->disableVirtualKeysUntil(when + mParameters.virtualKeyQuietTime);
+    if (mConfig->virtualKeyQuietTime > 0 && mCurrentTouch.pointerCount != 0) {
+        mContext->disableVirtualKeysUntil(when + mConfig->virtualKeyQuietTime);
     }
 }
 
@@ -3260,12 +3240,6 @@
 
 void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags,
         bool isTimeout) {
-    // Switch pointer presentation.
-    mPointerController->setPresentation(
-            mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
-                    ? PointerControllerInterface::PRESENTATION_SPOT
-                    : PointerControllerInterface::PRESENTATION_POINTER);
-
     // Update current gesture coordinates.
     bool cancelPreviousGesture, finishPreviousGesture;
     bool sendEvents = preparePointerGestures(when,
@@ -3274,6 +3248,12 @@
         return;
     }
 
+    // Switch pointer presentation.
+    mPointerController->setPresentation(
+            mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
+                    ? PointerControllerInterface::PRESENTATION_SPOT
+                    : PointerControllerInterface::PRESENTATION_POINTER);
+
     // Show or hide the pointer if needed.
     switch (mPointerGesture.currentGestureMode) {
     case PointerGesture::NEUTRAL:
@@ -3441,9 +3421,10 @@
 #endif
 
         if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
-            if (when <= mPointerGesture.tapUpTime + TAP_DRAG_INTERVAL) {
+            if (when <= mPointerGesture.tapUpTime + mConfig->pointerGestureTapDragInterval) {
                 // The tap/drag timeout has not yet expired.
-                getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime + TAP_DRAG_INTERVAL);
+                getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime
+                        + mConfig->pointerGestureTapDragInterval);
             } else {
                 // The tap is finished.
 #if DEBUG_GESTURES
@@ -3455,8 +3436,6 @@
                 mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
                 mPointerGesture.currentGestureIdBits.clear();
 
-                mPointerController->setButtonState(0);
-
                 if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
                     mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
                     mPointerGesture.spotIdBits.clear();
@@ -3514,7 +3493,7 @@
     if (activeTouchId < 0) {
         mPointerGesture.resetQuietTime();
     } else {
-        isQuietTime = when < mPointerGesture.quietTime + QUIET_INTERVAL;
+        isQuietTime = when < mPointerGesture.quietTime + mConfig->pointerGestureQuietInterval;
         if (!isQuietTime) {
             if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS
                     || mPointerGesture.lastGestureMode == PointerGesture::SWIPE
@@ -3551,8 +3530,6 @@
         mPointerGesture.currentGestureMode = PointerGesture::QUIET;
         mPointerGesture.currentGestureIdBits.clear();
 
-        mPointerController->setButtonState(0);
-
         if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
             mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
             mPointerGesture.spotIdBits.clear();
@@ -3587,7 +3564,7 @@
         if (activeTouchId >= 0) {
             if (mCurrentTouch.pointerCount > 1) {
                 int32_t bestId = -1;
-                float bestSpeed = DRAG_MIN_SWITCH_SPEED;
+                float bestSpeed = mConfig->pointerGestureDragMinSwitchSpeed;
                 for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
                     uint32_t id = mCurrentTouch.pointers[i].id;
                     float vx, vy;
@@ -3638,8 +3615,6 @@
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
 
-        mPointerController->setButtonState(BUTTON_STATE_PRIMARY);
-
         if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
             if (activeTouchId >= 0) {
                 // Collapse all spots into one point at the pointer location.
@@ -3667,21 +3642,23 @@
         *outFinishPreviousGesture = true;
 
         // Watch for taps coming out of HOVER or TAP_DRAG mode.
+        // Checking for taps after TAP_DRAG allows us to detect double-taps.
         bool tapped = false;
         if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER
                 || mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG)
                 && mLastTouch.pointerCount == 1) {
-            if (when <= mPointerGesture.tapDownTime + TAP_INTERVAL) {
+            if (when <= mPointerGesture.tapDownTime + mConfig->pointerGestureTapInterval) {
                 float x, y;
                 mPointerController->getPosition(&x, &y);
-                if (fabs(x - mPointerGesture.tapX) <= TAP_SLOP
-                        && fabs(y - mPointerGesture.tapY) <= TAP_SLOP) {
+                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 + TAP_DRAG_INTERVAL);
+                    getContext()->requestTimeoutAtTime(when
+                            + mConfig->pointerGestureTapDragInterval);
 
                     mPointerGesture.activeGestureId = 0;
                     mPointerGesture.currentGestureMode = PointerGesture::TAP;
@@ -3698,8 +3675,6 @@
                     mPointerGesture.currentGestureCoords[0].setAxisValue(
                             AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
 
-                    mPointerController->setButtonState(BUTTON_STATE_PRIMARY);
-
                     if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
                         mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_TAP;
                         mPointerGesture.spotIdBits.clear();
@@ -3733,8 +3708,6 @@
             mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
             mPointerGesture.currentGestureIdBits.clear();
 
-            mPointerController->setButtonState(0);
-
             if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
                 mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
                 mPointerGesture.spotIdBits.clear();
@@ -3750,11 +3723,11 @@
 
         mPointerGesture.currentGestureMode = PointerGesture::HOVER;
         if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
-            if (when <= mPointerGesture.tapUpTime + TAP_DRAG_INTERVAL) {
+            if (when <= mPointerGesture.tapUpTime + mConfig->pointerGestureTapDragInterval) {
                 float x, y;
                 mPointerController->getPosition(&x, &y);
-                if (fabs(x - mPointerGesture.tapX) <= TAP_SLOP
-                        && fabs(y - mPointerGesture.tapY) <= TAP_SLOP) {
+                if (fabs(x - mPointerGesture.tapX) <= mConfig->pointerGestureTapSlop
+                        && fabs(y - mPointerGesture.tapY) <= mConfig->pointerGestureTapSlop) {
                     mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
                 } else {
 #if DEBUG_GESTURES
@@ -3815,8 +3788,6 @@
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
                 down ? 1.0f : 0.0f);
 
-        mPointerController->setButtonState(down ? BUTTON_STATE_PRIMARY : 0);
-
         if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
             mPointerGesture.resetTap();
             mPointerGesture.tapDownTime = when;
@@ -3850,7 +3821,8 @@
         LOG_ASSERT(activeTouchId >= 0);
 
         bool needReference = false;
-        bool settled = when >= mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL;
+        bool settled = when >= mPointerGesture.firstTouchTime
+                + mConfig->pointerGestureMultitouchSettleInterval;
         if (mPointerGesture.lastGestureMode != PointerGesture::PRESS
                 && mPointerGesture.lastGestureMode != PointerGesture::SWIPE
                 && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
@@ -3944,14 +3916,15 @@
 
                 float speed1 = hypotf(vx1, vy1);
                 float speed2 = hypotf(vx2, vy2);
-                if (speed1 >= MULTITOUCH_MIN_SPEED && speed2 >= MULTITOUCH_MIN_SPEED) {
+                if (speed1 >= mConfig->pointerGestureMultitouchMinSpeed
+                        && speed2 >= mConfig->pointerGestureMultitouchMinSpeed) {
                     // Calculate the dot product of the velocity vectors.
                     // When the vectors are oriented in approximately the same direction,
                     // the angle betweeen them is near zero and the cosine of the angle
                     // approches 1.0.  Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
                     float dot = vx1 * vx2 + vy1 * vy2;
                     float cosine = dot / (speed1 * speed2); // denominator always > 0
-                    if (cosine >= SWIPE_TRANSITION_ANGLE_COSINE) {
+                    if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) {
                         // Pointers are moving in the same direction.  Switch to SWIPE.
 #if DEBUG_GESTURES
                         LOGD("Gestures: PRESS transitioned to SWIPE, "
@@ -4067,8 +4040,6 @@
                     mPointerGesture.referenceGestureY);
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
 
-            mPointerController->setButtonState(BUTTON_STATE_PRIMARY);
-
             if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
                 mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_PRESS;
             }
@@ -4091,8 +4062,6 @@
                     mPointerGesture.referenceGestureY);
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
 
-            mPointerController->setButtonState(0); // touch is not actually following the pointer
-
             if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
                 mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_SWIPE;
             }
@@ -4194,8 +4163,6 @@
 #endif
             }
 
-            mPointerController->setButtonState(0); // touch is not actually following the pointer
-
             if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
                 mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_FREEFORM;
             }
@@ -4225,6 +4192,8 @@
         }
     }
 
+    mPointerController->setButtonState(mCurrentTouch.buttonState);
+
 #if DEBUG_GESTURES
     LOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
             "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 0fbc93c..db4679b 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -40,6 +40,109 @@
 
 
 /*
+ * Input reader configuration.
+ *
+ * Specifies various options that modify the behavior of the input reader.
+ */
+struct InputReaderConfiguration {
+    // 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;
+
+    // Determines whether to turn on some hacks to improve touch interaction with another device
+    // where touch coordinate data can get corrupted.
+    bool filterJumpyTouchEvents;
+
+    // Gets the amount of time to disable virtual keys after the screen is touched
+    // in order to filter out accidental virtual key presses due to swiping gestures
+    // or taps near the edge of the display.  May be 0 to disable the feature.
+    nsecs_t virtualKeyQuietTime;
+
+    // The excluded device names for the platform.
+    // Devices with these names will be ignored.
+    Vector<String8> excludedDeviceNames;
+
+    // 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.
+    nsecs_t pointerGestureQuietInterval;
+
+    // The minimum speed that a pointer must travel for us to consider switching the active
+    // touch pointer to it during a drag.  This threshold is set to avoid switching due
+    // to noise from a finger resting on the touch pad (perhaps just pressing it down).
+    float pointerGestureDragMinSwitchSpeed; // in pixels per second
+
+    // Tap gesture delay time.
+    // The time between down and up must be less than this to be considered a tap.
+    nsecs_t pointerGestureTapInterval;
+
+    // Tap drag gesture delay time.
+    // The time between the previous tap's up and the next down must be less than
+    // this to be considered a drag.  Otherwise, the previous tap is finished and a
+    // new tap begins.
+    //
+    // Note that the previous tap will be held down for this entire duration so this
+    // interval must be shorter than the long press timeout.
+    nsecs_t pointerGestureTapDragInterval;
+
+    // The distance in pixels that the pointer is allowed to move from initial down
+    // to up and still be called a tap.
+    float pointerGestureTapSlop; // in pixels
+
+    // Time after the first touch points go down to settle on an initial centroid.
+    // This is intended to be enough time to handle cases where the user puts down two
+    // fingers at almost but not quite exactly the same time.
+    nsecs_t pointerGestureMultitouchSettleInterval;
+
+    // The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
+    // both of the pointers are moving at least this fast.
+    float pointerGestureMultitouchMinSpeed; // in pixels per second
+
+    // The transition from PRESS to SWIPE gesture mode can only occur when the
+    // cosine of the angle between the two vectors is greater than or equal to than this value
+    // which indicates that the vectors are oriented in the same direction.
+    // When the vectors are oriented in the exactly same direction, the cosine is 1.0.
+    // (In exactly opposite directions, the cosine is -1.0.)
+    float pointerGestureSwipeTransitionAngleCosine;
+
+    // The transition from PRESS to SWIPE gesture mode can only occur when the
+    // fingers are no more than this far apart relative to the diagonal size of
+    // the touch pad.  For example, a ratio of 0.5 means that the fingers must be
+    // no more than half the diagonal size of the touch pad apart.
+    float pointerGestureSwipeMaxWidthRatio;
+
+    // The gesture movement speed factor relative to the size of the display.
+    // Movement speed applies when the fingers are moving in the same direction.
+    // Without acceleration, a full swipe of the touch pad diagonal in movement mode
+    // will cover this portion of the display diagonal.
+    float pointerGestureMovementSpeedRatio;
+
+    // The gesture zoom speed factor relative to the size of the display.
+    // Zoom speed applies when the fingers are mostly moving relative to each other
+    // to execute a scale gesture or similar.
+    // Without acceleration, a full swipe of the touch pad diagonal in zoom mode
+    // will cover this portion of the display diagonal.
+    float pointerGestureZoomSpeedRatio;
+
+    InputReaderConfiguration() :
+            filterTouchEvents(false),
+            filterJumpyTouchEvents(false),
+            virtualKeyQuietTime(0),
+            pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
+            pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
+            pointerGestureTapInterval(150 * 1000000LL), // 150 ms
+            pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
+            pointerGestureTapSlop(10.0f), // 10 pixels
+            pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
+            pointerGestureMultitouchMinSpeed(150.0f), // 150 pixels per second
+            pointerGestureSwipeTransitionAngleCosine(0.5f), // cosine of 45degrees
+            pointerGestureSwipeMaxWidthRatio(0.333f),
+            pointerGestureMovementSpeedRatio(0.8f),
+            pointerGestureZoomSpeedRatio(0.3f) { }
+};
+
+
+/*
  * Input reader policy interface.
  *
  * The input reader policy is used by the input reader to interact with the Window Manager
@@ -68,24 +171,8 @@
     virtual bool getDisplayInfo(int32_t displayId,
             int32_t* width, int32_t* height, int32_t* orientation) = 0;
 
-    /* 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.
-     */
-    virtual bool filterTouchEvents() = 0;
-
-    /* Determines whether to turn on some hacks to improve touch interaction with another device
-     * where touch coordinate data can get corrupted.
-     */
-    virtual bool filterJumpyTouchEvents() = 0;
-
-    /* Gets the amount of time to disable virtual keys after the screen is touched
-     * in order to filter out accidental virtual key presses due to swiping gestures
-     * or taps near the edge of the display.  May be 0 to disable the feature.
-     */
-    virtual nsecs_t getVirtualKeyQuietTime() = 0;
-
-    /* Gets the excluded device names for the platform. */
-    virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0;
+    /* Gets the input reader configuration. */
+    virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) = 0;
 
     /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
     virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0;
@@ -162,6 +249,7 @@
     virtual void requestTimeoutAtTime(nsecs_t when) = 0;
 
     virtual InputReaderPolicyInterface* getPolicy() = 0;
+    virtual const InputReaderConfiguration* getConfig() = 0;
     virtual InputDispatcherInterface* getDispatcher() = 0;
     virtual EventHubInterface* getEventHub() = 0;
 };
@@ -212,7 +300,10 @@
     sp<InputReaderPolicyInterface> mPolicy;
     sp<InputDispatcherInterface> mDispatcher;
 
+    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(); }
 
@@ -353,6 +444,7 @@
     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(); }
 
@@ -665,6 +757,9 @@
     uint32_t mTouchSource; // sources when reporting touch data
     uint32_t mPointerSource; // sources when reporting pointer gestures
 
+    // The reader's configuration.
+    const InputReaderConfiguration* mConfig;
+
     // Immutable configuration parameters.
     struct Parameters {
         enum DeviceType {
@@ -680,7 +775,6 @@
         bool useBadTouchFilter;
         bool useJumpyTouchFilter;
         bool useAveragingTouchFilter;
-        nsecs_t virtualKeyQuietTime;
 
         enum GestureMode {
             GESTURE_MODE_POINTER,
@@ -939,6 +1033,8 @@
             // Exactly one finger dragging following a tap.
             // Pointer follows the active finger.
             // Emits DOWN, MOVE and UP events at the pointer location.
+            //
+            // Detect double-taps when the finger goes up while in TAP_DRAG mode.
             TAP_DRAG,
 
             // Button is pressed.
@@ -949,6 +1045,8 @@
             // Exactly one finger, button is not pressed.
             // Pointer follows the active finger.
             // Emits HOVER_MOVE events at the pointer location.
+            //
+            // Detect taps when the finger goes up while in HOVER mode.
             HOVER,
 
             // Exactly two fingers but neither have moved enough to clearly indicate
diff --git a/services/input/tests/InputDispatcher_test.cpp b/services/input/tests/InputDispatcher_test.cpp
index 2f846c4..0a29bc6 100644
--- a/services/input/tests/InputDispatcher_test.cpp
+++ b/services/input/tests/InputDispatcher_test.cpp
@@ -35,6 +35,8 @@
 // --- FakeInputDispatcherPolicy ---
 
 class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
+    InputDispatcherConfiguration mConfig;
+
 protected:
     virtual ~FakeInputDispatcherPolicy() {
     }
@@ -55,16 +57,12 @@
     virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle) {
     }
 
-    virtual nsecs_t getKeyRepeatTimeout() {
-        return 500 * 1000000LL;
+    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
+        *outConfig = mConfig;
     }
 
-    virtual nsecs_t getKeyRepeatDelay() {
-        return 50 * 1000000LL;
-    }
-
-    virtual int32_t getMaxEventsPerSecond() {
-        return 60;
+    virtual bool isKeyRepeatEnabled() {
+        return true;
     }
 
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) {
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 8b3e731..b5f603a 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -120,17 +120,14 @@
     };
 
     KeyedVector<int32_t, DisplayInfo> mDisplayInfos;
-    bool mFilterTouchEvents;
-    bool mFilterJumpyTouchEvents;
-    Vector<String8> mExcludedDeviceNames;
+    InputReaderConfiguration mConfig;
     KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
 
 protected:
     virtual ~FakeInputReaderPolicy() { }
 
 public:
-    FakeInputReaderPolicy() :
-            mFilterTouchEvents(false), mFilterJumpyTouchEvents(false) {
+    FakeInputReaderPolicy() {
     }
 
     void removeDisplayInfo(int32_t displayId) {
@@ -148,11 +145,11 @@
     }
 
     void setFilterTouchEvents(bool enabled) {
-        mFilterTouchEvents = enabled;
+        mConfig.filterTouchEvents = enabled;
     }
 
     void setFilterJumpyTouchEvents(bool enabled) {
-        mFilterJumpyTouchEvents = enabled;
+        mConfig.filterJumpyTouchEvents = enabled;
     }
 
     virtual nsecs_t getVirtualKeyQuietTime() {
@@ -160,7 +157,7 @@
     }
 
     void addExcludedDeviceName(const String8& deviceName) {
-        mExcludedDeviceNames.push(deviceName);
+        mConfig.excludedDeviceNames.push(deviceName);
     }
 
     void setPointerController(int32_t deviceId, const sp<FakePointerController>& controller) {
@@ -187,16 +184,8 @@
         return false;
     }
 
-    virtual bool filterTouchEvents() {
-        return mFilterTouchEvents;
-    }
-
-    virtual bool filterJumpyTouchEvents() {
-        return mFilterJumpyTouchEvents;
-    }
-
-    virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
-        outExcludedDeviceNames.appendVector(mExcludedDeviceNames);
+    virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) {
+        *outConfig = mConfig;
     }
 
     virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
@@ -739,6 +728,8 @@
     int32_t mGlobalMetaState;
     bool mUpdateGlobalMetaStateWasCalled;
 
+    InputReaderConfiguration mConfig;
+
 public:
     FakeInputReaderContext(const sp<EventHubInterface>& eventHub,
             const sp<InputReaderPolicyInterface>& policy,
@@ -776,6 +767,11 @@
         return mPolicy.get();
     }
 
+    virtual const InputReaderConfiguration* getConfig() {
+        mPolicy->getReaderConfiguration(&mConfig);
+        return &mConfig;
+    }
+
     virtual InputDispatcherInterface* getDispatcher() {
         return mDispatcher.get();
     }
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index a4a95a0..65f8b34 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -29,6 +29,7 @@
 import java.util.List;
 import java.util.Locale;
 
+import org.apache.commons.logging.impl.SimpleLog;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 080cbda..4cc032f 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -12217,6 +12217,15 @@
                     ac.updateConfiguration(mConfiguration);
                 }
 
+                // Make sure all resources in our process are updated
+                // right now, so that anyone who is going to retrieve
+                // resource values after we return will be sure to get
+                // the new ones.  This is especially important during
+                // boot, where the first config change needs to guarantee
+                // all resources have that config before following boot
+                // code is executed.
+                mSystemThread.applyConfigurationToResources(newConfig);
+
                 if (Settings.System.hasInterestingConfigurationChanges(changes)) {
                     Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
                     msg.obj = new Configuration(mConfiguration);
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 65ea4f4..2706d49 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1455,6 +1455,8 @@
                     if (SHOW_APP_STARTING_PREVIEW && mMainStack) {
                         mService.mWindowManager.setAppStartingWindow(
                                 next, next.packageName, next.theme,
+                                mService.compatibilityInfoForPackageLocked(
+                                        next.info.applicationInfo),
                                 next.nonLocalizedLabel,
                                 next.labelRes, next.icon, next.windowFlags,
                                 null, true);
@@ -1491,6 +1493,8 @@
                 if (SHOW_APP_STARTING_PREVIEW) {
                     mService.mWindowManager.setAppStartingWindow(
                             next, next.packageName, next.theme,
+                            mService.compatibilityInfoForPackageLocked(
+                                    next.info.applicationInfo),
                             next.nonLocalizedLabel,
                             next.labelRes, next.icon, next.windowFlags,
                             null, true);
@@ -1617,7 +1621,9 @@
                     else if (prev.nowVisible) prev = null;
                 }
                 mService.mWindowManager.setAppStartingWindow(
-                        r, r.packageName, r.theme, r.nonLocalizedLabel,
+                        r, r.packageName, r.theme,
+                        mService.compatibilityInfoForPackageLocked(
+                                r.info.applicationInfo), r.nonLocalizedLabel,
                         r.labelRes, r.icon, r.windowFlags, prev, showStartingIcon);
             }
         } else {
diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java
index 8949f48..1334bcd 100644
--- a/services/java/com/android/server/am/CompatModePackages.java
+++ b/services/java/com/android/server/am/CompatModePackages.java
@@ -150,9 +150,11 @@
     }
 
     public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
-        return new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
+        CompatibilityInfo ci = new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
                 mService.mConfiguration.smallestScreenWidthDp,
                 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
+        //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
+        return ci;
     }
 
     public int computeCompatModeLocked(ApplicationInfo ai) {
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index becd44a..ab781f4 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -496,6 +496,26 @@
         }
 
         @SuppressWarnings("unused")
+        public int getTapTimeout() {
+            return ViewConfiguration.getTapTimeout();
+        }
+
+        @SuppressWarnings("unused")
+        public int getDoubleTapTimeout() {
+            return ViewConfiguration.getDoubleTapTimeout();
+        }
+
+        @SuppressWarnings("unused")
+        public int getLongPressTimeout() {
+            return ViewConfiguration.getLongPressTimeout();
+        }
+
+        @SuppressWarnings("unused")
+        public int getTouchSlop() {
+            return ViewConfiguration.get(mContext).getScaledTouchSlop();
+        }
+
+        @SuppressWarnings("unused")
         public int getMaxEventsPerSecond() {
             int result = 0;
             try {
diff --git a/services/java/com/android/server/wm/StartingData.java b/services/java/com/android/server/wm/StartingData.java
index 625fcfe..46bb480 100644
--- a/services/java/com/android/server/wm/StartingData.java
+++ b/services/java/com/android/server/wm/StartingData.java
@@ -16,18 +16,23 @@
 
 package com.android.server.wm;
 
+import android.content.res.CompatibilityInfo;
+
 final class StartingData {
     final String pkg;
     final int theme;
+    final CompatibilityInfo compatInfo;
     final CharSequence nonLocalizedLabel;
     final int labelRes;
     final int icon;
     final int windowFlags;
 
-    StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel,
+    StartingData(String _pkg, int _theme, CompatibilityInfo _compatInfo,
+            CharSequence _nonLocalizedLabel,
             int _labelRes, int _icon, int _windowFlags) {
         pkg = _pkg;
         theme = _theme;
+        compatInfo = _compatInfo;
         nonLocalizedLabel = _nonLocalizedLabel;
         labelRes = _labelRes;
         icon = _icon;
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 9c98296..7760897 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -602,8 +602,7 @@
 
     final Configuration mTempConfiguration = new Configuration();
 
-    // The frame use to limit the size of the app running in compatibility mode.
-    Rect mCompatibleScreenFrame = new Rect();
+    // The desired scaling factor for compatible apps.
     float mCompatibleScreenScale;
 
     public static WindowManagerService main(Context context,
@@ -3526,7 +3525,8 @@
     }
 
     public void setAppStartingWindow(IBinder token, String pkg,
-            int theme, CharSequence nonLocalizedLabel, int labelRes, int icon,
+            int theme, CompatibilityInfo compatInfo,
+            CharSequence nonLocalizedLabel, int labelRes, int icon,
             int windowFlags, IBinder transferFrom, boolean createIfNeeded) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setAppStartingIcon()")) {
@@ -3676,8 +3676,7 @@
             }
 
             mStartingIconInTransition = true;
-            wtoken.startingData = new StartingData(
-                    pkg, theme, nonLocalizedLabel,
+            wtoken.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
                     labelRes, icon, windowFlags);
             Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
             // Note: we really want to do sendMessageAtFrontOfQueue() because we
@@ -5562,8 +5561,7 @@
         dm.heightPixels = dm.unscaledHeightPixels = mAppDisplayHeight
                 = mPolicy.getNonDecorDisplayHeight(mRotation, dh);
 
-        mCompatibleScreenScale = CompatibilityInfo.updateCompatibleScreenFrame(
-                dm, mCompatibleScreenFrame, null);
+        mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(dm, null);
 
         config.screenWidthDp = (int)(mPolicy.getConfigDisplayWidth(mRotation, dw) / dm.density);
         config.screenHeightDp = (int)(mPolicy.getConfigDisplayHeight(mRotation, dh) / dm.density);
@@ -6138,9 +6136,8 @@
                     View view = null;
                     try {
                         view = mPolicy.addStartingWindow(
-                            wtoken.token, sd.pkg,
-                            sd.theme, sd.nonLocalizedLabel, sd.labelRes,
-                            sd.icon, sd.windowFlags);
+                            wtoken.token, sd.pkg, sd.theme, sd.compatInfo,
+                            sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.windowFlags);
                     } catch (Exception e) {
                         Slog.w(TAG, "Exception when adding starting window", e);
                     }
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index dde4776..fef41c9 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -72,6 +72,10 @@
     jmethodID getKeyRepeatTimeout;
     jmethodID getKeyRepeatDelay;
     jmethodID getMaxEventsPerSecond;
+    jmethodID getTapTimeout;
+    jmethodID getDoubleTapTimeout;
+    jmethodID getLongPressTimeout;
+    jmethodID getTouchSlop;
     jmethodID getPointerLayer;
     jmethodID getPointerIcon;
 } gCallbacksClassInfo;
@@ -107,6 +111,16 @@
 
 // --- Global functions ---
 
+template<typename T>
+inline static T min(const T& a, const T& b) {
+    return a < b ? a : b;
+}
+
+template<typename T>
+inline static T max(const T& a, const T& b) {
+    return a > b ? a : b;
+}
+
 static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
         const sp<InputApplicationHandle>& inputApplicationHandle) {
     if (inputApplicationHandle == NULL) {
@@ -170,10 +184,7 @@
 
     virtual bool getDisplayInfo(int32_t displayId,
             int32_t* width, int32_t* height, int32_t* orientation);
-    virtual bool filterTouchEvents();
-    virtual bool filterJumpyTouchEvents();
-    virtual nsecs_t getVirtualKeyQuietTime();
-    virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
+    virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
     virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
 
     /* --- InputDispatcherPolicyInterface implementation --- */
@@ -184,9 +195,8 @@
     virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
             const sp<InputWindowHandle>& inputWindowHandle);
     virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle);
-    virtual nsecs_t getKeyRepeatTimeout();
-    virtual nsecs_t getKeyRepeatDelay();
-    virtual int32_t getMaxEventsPerSecond();
+    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
+    virtual bool isKeyRepeatEnabled();
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
     virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
     virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
@@ -208,18 +218,6 @@
     jobject mCallbacksObj;
     sp<Looper> mLooper;
 
-    // Cached filtering policies.
-    int32_t mFilterTouchEvents;
-    int32_t mFilterJumpyTouchEvents;
-    nsecs_t mVirtualKeyQuietTime;
-
-    // Cached key repeat policy.
-    nsecs_t mKeyRepeatTimeout;
-    nsecs_t mKeyRepeatDelay;
-
-    // Cached throttling policy.
-    int32_t mMaxEventsPerSecond;
-
     Mutex mLock;
     struct Locked {
         // Display size information.
@@ -255,10 +253,7 @@
 
 NativeInputManager::NativeInputManager(jobject contextObj,
         jobject callbacksObj, const sp<Looper>& looper) :
-        mLooper(looper),
-        mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1),
-        mKeyRepeatTimeout(-1), mKeyRepeatDelay(-1),
-        mMaxEventsPerSecond(-1) {
+        mLooper(looper) {
     JNIEnv* env = jniEnv();
 
     mContextObj = env->NewGlobalRef(contextObj);
@@ -371,73 +366,68 @@
     return result;
 }
 
-bool NativeInputManager::filterTouchEvents() {
-    if (mFilterTouchEvents < 0) {
-        JNIEnv* env = jniEnv();
-
-        jboolean result = env->CallBooleanMethod(mCallbacksObj,
-                gCallbacksClassInfo.filterTouchEvents);
-        if (checkAndClearExceptionFromCallback(env, "filterTouchEvents")) {
-            result = false;
-        }
-
-        mFilterTouchEvents = result ? 1 : 0;
-    }
-    return mFilterTouchEvents;
-}
-
-bool NativeInputManager::filterJumpyTouchEvents() {
-    if (mFilterJumpyTouchEvents < 0) {
-        JNIEnv* env = jniEnv();
-
-        jboolean result = env->CallBooleanMethod(mCallbacksObj,
-                gCallbacksClassInfo.filterJumpyTouchEvents);
-        if (checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) {
-            result = false;
-        }
-
-        mFilterJumpyTouchEvents = result ? 1 : 0;
-    }
-    return mFilterJumpyTouchEvents;
-}
-
-nsecs_t NativeInputManager::getVirtualKeyQuietTime() {
-    if (mVirtualKeyQuietTime < 0) {
-        JNIEnv* env = jniEnv();
-
-        jint result = env->CallIntMethod(mCallbacksObj,
-                gCallbacksClassInfo.getVirtualKeyQuietTimeMillis);
-        if (checkAndClearExceptionFromCallback(env, "getVirtualKeyQuietTimeMillis")) {
-            result = 0;
-        }
-        if (result < 0) {
-            result = 0;
-        }
-
-        mVirtualKeyQuietTime = milliseconds_to_nanoseconds(result);
-    }
-    return mVirtualKeyQuietTime;
-}
-
-void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
-    outExcludedDeviceNames.clear();
-
+void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) {
     JNIEnv* env = jniEnv();
 
-    jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+    jboolean filterTouchEvents = env->CallBooleanMethod(mCallbacksObj,
+            gCallbacksClassInfo.filterTouchEvents);
+    if (!checkAndClearExceptionFromCallback(env, "filterTouchEvents")) {
+        outConfig->filterTouchEvents = filterTouchEvents;
+    }
+
+    jboolean filterJumpyTouchEvents = env->CallBooleanMethod(mCallbacksObj,
+            gCallbacksClassInfo.filterJumpyTouchEvents);
+    if (!checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) {
+        outConfig->filterJumpyTouchEvents = filterJumpyTouchEvents;
+    }
+
+    jint virtualKeyQuietTime = env->CallIntMethod(mCallbacksObj,
+            gCallbacksClassInfo.getVirtualKeyQuietTimeMillis);
+    if (!checkAndClearExceptionFromCallback(env, "getVirtualKeyQuietTimeMillis")) {
+        outConfig->virtualKeyQuietTime = milliseconds_to_nanoseconds(virtualKeyQuietTime);
+    }
+
+    outConfig->excludedDeviceNames.clear();
+    jobjectArray excludedDeviceNames = jobjectArray(env->CallObjectMethod(mCallbacksObj,
             gCallbacksClassInfo.getExcludedDeviceNames));
-    if (! checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && result) {
-        jsize length = env->GetArrayLength(result);
+    if (!checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && excludedDeviceNames) {
+        jsize length = env->GetArrayLength(excludedDeviceNames);
         for (jsize i = 0; i < length; i++) {
-            jstring item = jstring(env->GetObjectArrayElement(result, i));
-
+            jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i));
             const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
-            outExcludedDeviceNames.add(String8(deviceNameChars));
+            outConfig->excludedDeviceNames.add(String8(deviceNameChars));
             env->ReleaseStringUTFChars(item, deviceNameChars);
-
             env->DeleteLocalRef(item);
         }
-        env->DeleteLocalRef(result);
+        env->DeleteLocalRef(excludedDeviceNames);
+    }
+
+    jint tapTimeout = env->CallIntMethod(mCallbacksObj,
+            gCallbacksClassInfo.getTapTimeout);
+    if (!checkAndClearExceptionFromCallback(env, "getTapTimeout")) {
+        jint doubleTapTimeout = env->CallIntMethod(mCallbacksObj,
+                gCallbacksClassInfo.getDoubleTapTimeout);
+        if (!checkAndClearExceptionFromCallback(env, "getDoubleTapTimeout")) {
+            jint longPressTimeout = env->CallIntMethod(mCallbacksObj,
+                    gCallbacksClassInfo.getLongPressTimeout);
+            if (!checkAndClearExceptionFromCallback(env, "getLongPressTimeout")) {
+                outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(tapTimeout);
+
+                // We must ensure that the tap-drag interval is significantly shorter than
+                // the long-press timeout because the tap is held down for the entire duration
+                // of the double-tap timeout.
+                jint tapDragInterval = max(min(longPressTimeout - 100,
+                        doubleTapTimeout), tapTimeout);
+                outConfig->pointerGestureTapDragInterval =
+                        milliseconds_to_nanoseconds(tapDragInterval);
+            }
+        }
+    }
+
+    jint touchSlop = env->CallIntMethod(mCallbacksObj,
+            gCallbacksClassInfo.getTouchSlop);
+    if (!checkAndClearExceptionFromCallback(env, "getTouchSlop")) {
+        outConfig->pointerGestureTapSlop = touchSlop;
     }
 }
 
@@ -559,54 +549,31 @@
     }
 }
 
-nsecs_t NativeInputManager::getKeyRepeatTimeout() {
-    if (! isScreenOn()) {
-        // Disable key repeat when the screen is off.
-        return -1;
-    } else {
-        if (mKeyRepeatTimeout < 0) {
-            JNIEnv* env = jniEnv();
+void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
+    JNIEnv* env = jniEnv();
 
-            jint result = env->CallIntMethod(mCallbacksObj,
-                    gCallbacksClassInfo.getKeyRepeatTimeout);
-            if (checkAndClearExceptionFromCallback(env, "getKeyRepeatTimeout")) {
-                result = 500;
-            }
+    jint keyRepeatTimeout = env->CallIntMethod(mCallbacksObj,
+            gCallbacksClassInfo.getKeyRepeatTimeout);
+    if (!checkAndClearExceptionFromCallback(env, "getKeyRepeatTimeout")) {
+        outConfig->keyRepeatTimeout = milliseconds_to_nanoseconds(keyRepeatTimeout);
+    }
 
-            mKeyRepeatTimeout = milliseconds_to_nanoseconds(result);
-        }
-        return mKeyRepeatTimeout;
+    jint keyRepeatDelay = env->CallIntMethod(mCallbacksObj,
+            gCallbacksClassInfo.getKeyRepeatDelay);
+    if (!checkAndClearExceptionFromCallback(env, "getKeyRepeatDelay")) {
+        outConfig->keyRepeatDelay = milliseconds_to_nanoseconds(keyRepeatDelay);
+    }
+
+    jint maxEventsPerSecond = env->CallIntMethod(mCallbacksObj,
+            gCallbacksClassInfo.getMaxEventsPerSecond);
+    if (!checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) {
+        outConfig->maxEventsPerSecond = maxEventsPerSecond;
     }
 }
 
-nsecs_t NativeInputManager::getKeyRepeatDelay() {
-    if (mKeyRepeatDelay < 0) {
-        JNIEnv* env = jniEnv();
-
-        jint result = env->CallIntMethod(mCallbacksObj,
-                gCallbacksClassInfo.getKeyRepeatDelay);
-        if (checkAndClearExceptionFromCallback(env, "getKeyRepeatDelay")) {
-            result = 50;
-        }
-
-        mKeyRepeatDelay = milliseconds_to_nanoseconds(result);
-    }
-    return mKeyRepeatDelay;
-}
-
-int32_t NativeInputManager::getMaxEventsPerSecond() {
-    if (mMaxEventsPerSecond < 0) {
-        JNIEnv* env = jniEnv();
-
-        jint result = env->CallIntMethod(mCallbacksObj,
-                gCallbacksClassInfo.getMaxEventsPerSecond);
-        if (checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) {
-            result = 60;
-        }
-
-        mMaxEventsPerSecond = result;
-    }
-    return mMaxEventsPerSecond;
+bool NativeInputManager::isKeyRepeatEnabled() {
+    // Only enable automatic key repeating when the screen is on.
+    return isScreenOn();
 }
 
 void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
@@ -1342,6 +1309,18 @@
     GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, gCallbacksClassInfo.clazz,
             "getKeyRepeatDelay", "()I");
 
+    GET_METHOD_ID(gCallbacksClassInfo.getTapTimeout, gCallbacksClassInfo.clazz,
+            "getTapTimeout", "()I");
+
+    GET_METHOD_ID(gCallbacksClassInfo.getDoubleTapTimeout, gCallbacksClassInfo.clazz,
+            "getDoubleTapTimeout", "()I");
+
+    GET_METHOD_ID(gCallbacksClassInfo.getLongPressTimeout, gCallbacksClassInfo.clazz,
+            "getLongPressTimeout", "()I");
+
+    GET_METHOD_ID(gCallbacksClassInfo.getTouchSlop, gCallbacksClassInfo.clazz,
+            "getTouchSlop", "()I");
+
     GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz,
             "getMaxEventsPerSecond", "()I");
 
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index e0f0f05..f015378 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -164,7 +164,7 @@
         }
         
         try {
-            mWm.setAppStartingWindow(null, "foo", 0, null, 0, 0, 0, null, false);
+            mWm.setAppStartingWindow(null, "foo", 0, null, null, 0, 0, 0, null, false);
             fail("IWindowManager.setAppStartingWindow did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {