Merge "Fix accessibility actions in AbsListView." into jb-dev
diff --git a/api/16.txt b/api/16.txt
index 2c02347..6aab939 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -9243,7 +9243,6 @@
     method public int getMinimumWidth();
     method public abstract int getOpacity();
     method public boolean getPadding(android.graphics.Rect);
-    method public int getResolvedLayoutDirectionSelf();
     method public int[] getState();
     method public android.graphics.Region getTransparentRegion();
     method public void inflate(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -24513,14 +24512,14 @@
   public class ViewDebug {
     ctor public ViewDebug();
     method public static void dumpCapturedView(java.lang.String, java.lang.Object);
-    method public static void startHierarchyTracing(java.lang.String, android.view.View);
-    method public static void startRecyclerTracing(java.lang.String, android.view.View);
-    method public static void stopHierarchyTracing();
-    method public static void stopRecyclerTracing();
-    method public static void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...);
-    method public static void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
-    field public static final boolean TRACE_HIERARCHY = false;
-    field public static final boolean TRACE_RECYCLER = false;
+    method public static deprecated void startHierarchyTracing(java.lang.String, android.view.View);
+    method public static deprecated void startRecyclerTracing(java.lang.String, android.view.View);
+    method public static deprecated void stopHierarchyTracing();
+    method public static deprecated void stopRecyclerTracing();
+    method public static deprecated void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...);
+    method public static deprecated void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
+    field public static final deprecated boolean TRACE_HIERARCHY = false;
+    field public static final deprecated boolean TRACE_RECYCLER = false;
   }
 
   public static abstract class ViewDebug.CapturedViewProperty implements java.lang.annotation.Annotation {
@@ -24532,7 +24531,7 @@
   public static abstract class ViewDebug.FlagToString implements java.lang.annotation.Annotation {
   }
 
-  public static final class ViewDebug.HierarchyTraceType extends java.lang.Enum {
+  public static final deprecated class ViewDebug.HierarchyTraceType extends java.lang.Enum {
     method public static android.view.ViewDebug.HierarchyTraceType valueOf(java.lang.String);
     method public static final android.view.ViewDebug.HierarchyTraceType[] values();
     enum_constant public static final android.view.ViewDebug.HierarchyTraceType BUILD_CACHE;
@@ -24548,7 +24547,7 @@
   public static abstract class ViewDebug.IntToString implements java.lang.annotation.Annotation {
   }
 
-  public static final class ViewDebug.RecyclerTraceType extends java.lang.Enum {
+  public static final deprecated class ViewDebug.RecyclerTraceType extends java.lang.Enum {
     method public static android.view.ViewDebug.RecyclerTraceType valueOf(java.lang.String);
     method public static final android.view.ViewDebug.RecyclerTraceType[] values();
     enum_constant public static final android.view.ViewDebug.RecyclerTraceType BIND_VIEW;
@@ -27329,7 +27328,7 @@
     field public int gravity;
   }
 
-  public class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
+  public deprecated class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
     ctor public Gallery(android.content.Context);
     ctor public Gallery(android.content.Context, android.util.AttributeSet);
     ctor public Gallery(android.content.Context, android.util.AttributeSet, int);
@@ -28620,8 +28619,6 @@
     method public void onEndBatchEdit();
     method public boolean onPreDraw();
     method public boolean onPrivateIMECommand(java.lang.String, android.os.Bundle);
-    method public void onResolvedLayoutDirectionReset();
-    method public void onResolvedTextDirectionChanged();
     method public void onRestoreInstanceState(android.os.Parcelable);
     method public android.os.Parcelable onSaveInstanceState();
     method protected void onSelectionChanged(int, int);
diff --git a/api/current.txt b/api/current.txt
index 2c02347..6aab939 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9243,7 +9243,6 @@
     method public int getMinimumWidth();
     method public abstract int getOpacity();
     method public boolean getPadding(android.graphics.Rect);
-    method public int getResolvedLayoutDirectionSelf();
     method public int[] getState();
     method public android.graphics.Region getTransparentRegion();
     method public void inflate(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -24513,14 +24512,14 @@
   public class ViewDebug {
     ctor public ViewDebug();
     method public static void dumpCapturedView(java.lang.String, java.lang.Object);
-    method public static void startHierarchyTracing(java.lang.String, android.view.View);
-    method public static void startRecyclerTracing(java.lang.String, android.view.View);
-    method public static void stopHierarchyTracing();
-    method public static void stopRecyclerTracing();
-    method public static void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...);
-    method public static void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
-    field public static final boolean TRACE_HIERARCHY = false;
-    field public static final boolean TRACE_RECYCLER = false;
+    method public static deprecated void startHierarchyTracing(java.lang.String, android.view.View);
+    method public static deprecated void startRecyclerTracing(java.lang.String, android.view.View);
+    method public static deprecated void stopHierarchyTracing();
+    method public static deprecated void stopRecyclerTracing();
+    method public static deprecated void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...);
+    method public static deprecated void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
+    field public static final deprecated boolean TRACE_HIERARCHY = false;
+    field public static final deprecated boolean TRACE_RECYCLER = false;
   }
 
   public static abstract class ViewDebug.CapturedViewProperty implements java.lang.annotation.Annotation {
@@ -24532,7 +24531,7 @@
   public static abstract class ViewDebug.FlagToString implements java.lang.annotation.Annotation {
   }
 
-  public static final class ViewDebug.HierarchyTraceType extends java.lang.Enum {
+  public static final deprecated class ViewDebug.HierarchyTraceType extends java.lang.Enum {
     method public static android.view.ViewDebug.HierarchyTraceType valueOf(java.lang.String);
     method public static final android.view.ViewDebug.HierarchyTraceType[] values();
     enum_constant public static final android.view.ViewDebug.HierarchyTraceType BUILD_CACHE;
@@ -24548,7 +24547,7 @@
   public static abstract class ViewDebug.IntToString implements java.lang.annotation.Annotation {
   }
 
-  public static final class ViewDebug.RecyclerTraceType extends java.lang.Enum {
+  public static final deprecated class ViewDebug.RecyclerTraceType extends java.lang.Enum {
     method public static android.view.ViewDebug.RecyclerTraceType valueOf(java.lang.String);
     method public static final android.view.ViewDebug.RecyclerTraceType[] values();
     enum_constant public static final android.view.ViewDebug.RecyclerTraceType BIND_VIEW;
@@ -27329,7 +27328,7 @@
     field public int gravity;
   }
 
-  public class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
+  public deprecated class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
     ctor public Gallery(android.content.Context);
     ctor public Gallery(android.content.Context, android.util.AttributeSet);
     ctor public Gallery(android.content.Context, android.util.AttributeSet, int);
@@ -28620,8 +28619,6 @@
     method public void onEndBatchEdit();
     method public boolean onPreDraw();
     method public boolean onPrivateIMECommand(java.lang.String, android.os.Bundle);
-    method public void onResolvedLayoutDirectionReset();
-    method public void onResolvedTextDirectionChanged();
     method public void onRestoreInstanceState(android.os.Parcelable);
     method public android.os.Parcelable onSaveInstanceState();
     method protected void onSelectionChanged(int, int);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 8cd8900..cb53422 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -645,10 +645,6 @@
         String process = null;
         
         String cmd = nextArgRequired();
-        if ("looper".equals(cmd)) {
-            cmd = nextArgRequired();
-            profileType = 1;
-        }
 
         if ("start".equals(cmd)) {
             start = true;
@@ -1295,8 +1291,8 @@
                 "       am broadcast <INTENT>\n" +
                 "       am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
                 "               [--no-window-animation] <COMPONENT>\n" +
-                "       am profile [looper] start <PROCESS> <FILE>\n" +
-                "       am profile [looper] stop [<PROCESS>]\n" +
+                "       am profile start <PROCESS> <FILE>\n" +
+                "       am profile stop [<PROCESS>]\n" +
                 "       am dumpheap [flags] <PROCESS> <FILE>\n" +
                 "       am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
                 "       am clear-debug-app\n" +
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 69ee434..f20fd33 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3273,7 +3273,7 @@
         if (mMenuInflater == null) {
             initActionBar();
             if (mActionBar != null) {
-                mMenuInflater = new MenuInflater(mActionBar.getThemedContext());
+                mMenuInflater = new MenuInflater(mActionBar.getThemedContext(), this);
             } else {
                 mMenuInflater = new MenuInflater(this);
             }
@@ -4999,7 +4999,8 @@
         mCurrentConfig = config;
     }
 
-    final IBinder getActivityToken() {
+    /** @hide */
+    public final IBinder getActivityToken() {
         return mParent != null ? mParent.getActivityToken() : mToken;
     }
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4e61c3c..17b1962 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -26,7 +26,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
-import android.content.res.Configuration;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Point;
@@ -36,16 +36,17 @@
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.os.UserId;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -1798,6 +1799,40 @@
         }
     }
 
+    /** @hide */
+    public static int checkComponentPermission(String permission, int uid,
+            int owningUid, boolean exported) {
+        // Root, system server get to do everything.
+        if (uid == 0 || uid == Process.SYSTEM_UID) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        // Isolated processes don't get any permissions.
+        if (UserId.isIsolated(uid)) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+        // If there is a uid that owns whatever is being accessed, it has
+        // blanket access to it regardless of the permissions it requires.
+        if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        // If the target is not exported, then nobody else can get to it.
+        if (!exported) {
+            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
+            return PackageManager.PERMISSION_DENIED;
+        }
+        if (permission == null) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        try {
+            return AppGlobals.getPackageManager()
+                    .checkUidPermission(permission, uid);
+        } catch (RemoteException e) {
+            // Should never happen, but if it does... deny!
+            Slog.e(TAG, "PackageManager is dead?!?", e);
+        }
+        return PackageManager.PERMISSION_DENIED;
+    }
+
     /**
      * Returns the usage statistics of each installed package.
      *
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 2f2918d..4506546 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1656,6 +1656,15 @@
             return true;
         }
 
+        case GET_LAUNCHED_FROM_UID_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            int res = getLaunchedFromUid(token);
+            reply.writeNoException();
+            reply.writeInt(res);
+            return true;
+        }
+
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3785,5 +3794,18 @@
         return result;
     }
 
+    public int getLaunchedFromUid(IBinder activityToken) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(activityToken);
+        mRemote.transact(GET_LAUNCHED_FROM_UID_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int result = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return result;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b29035d..33e639e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3751,9 +3751,6 @@
         if (start) {
             try {
                 switch (profileType) {
-                    case 1:
-                        ViewDebug.startLooperProfiling(pcd.path, pcd.fd.getFileDescriptor());
-                        break;
                     default:                        
                         mProfiler.setProfiler(pcd.path, pcd.fd);
                         mProfiler.autoStopProfiler = false;
@@ -3772,9 +3769,6 @@
             }
         } else {
             switch (profileType) {
-                case 1:
-                    ViewDebug.stopLooperProfiling();
-                    break;
                 default:
                     mProfiler.stopProfiling();
                     break;
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index a2c7fa4..cf304df 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -350,6 +350,10 @@
     public boolean navigateUpTo(IBinder token, Intent target, int resultCode, Intent resultData)
             throws RemoteException;
 
+    // This is not public because you need to be very careful in how you
+    // manage your activity to make sure it is always the uid you expect.
+    public int getLaunchedFromUid(IBinder activityToken) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -592,4 +596,5 @@
     int NAVIGATE_UP_TO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+146;
     int SET_LOCK_SCREEN_SHOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+147;
     int FINISH_ACTIVITY_AFFINITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+148;
+    int GET_LAUNCHED_FROM_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+149;
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2eea171..c7afd2b2 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1850,7 +1850,7 @@
             contentView.setViewVisibility(R.id.text2, View.GONE);
 
             int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
-                    R.id.inbox_text4};
+                    R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
 
             // Make sure all rows are gone in case we reuse a view.
             for (int rowId : rowIds) {
@@ -1867,6 +1867,12 @@
                 i++;
             }
 
+            if  (mTexts.size() > rowIds.length) {
+                contentView.setViewVisibility(R.id.inbox_more, View.VISIBLE);
+            } else {
+                contentView.setViewVisibility(R.id.inbox_more, View.GONE);
+            }
+
             return contentView;
         }
 
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index c682852..c630bb5 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -630,7 +630,20 @@
      * Various types of objects will be returned depending on the underlying
      * resource -- for example, a solid color, PNG image, scalable image, etc.
      * The Drawable API hides these implementation details.
-     * 
+     *
+     * <p class="note"><strong>Note:</strong> Prior to
+     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, this function
+     * would not correctly retrieve the final configuration density when
+     * the resource ID passed here is an alias to another Drawable resource.
+     * This means that if the density configuration of the alias resource
+     * is different than the actual resource, the density of the returned
+     * Drawable would be incorrect, resulting in bad scaling.  To work
+     * around this, you can instead retrieve the Drawable through
+     * {@link TypedArray#getDrawable TypedArray.getDrawable}.  Use
+     * {@link android.content.Context#obtainStyledAttributes(int[])
+     * Context.obtainStyledAttributes} with
+     * an array containing the resource ID of interest to create the TypedArray.</p>
+     *
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
      *           entry. The value 0 is an invalid identifier.
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 3137947..9b6f82a 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -41,8 +41,13 @@
     // Keyboard layouts configuration.
     KeyboardLayout[] getKeyboardLayouts();
     KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor);
-    String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor);
-    void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor);
+    void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor);
+    String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor);
+    void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor);
+    void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
             String keyboardLayoutDescriptor);
 
     // Registers an input devices changed listener.
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index dfd35e1..262d87d 100755
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,6 +16,8 @@
 
 package android.hardware.input;
 
+import com.android.internal.util.ArrayUtils;
+
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.Context;
@@ -217,6 +219,41 @@
     }
 
     /**
+     * Gets information about the input device with the specified descriptor.
+     * @param descriptor The input device descriptor.
+     * @return The input device or null if not found.
+     * @hide
+     */
+    public InputDevice getInputDeviceByDescriptor(String descriptor) {
+        if (descriptor == null) {
+            throw new IllegalArgumentException("descriptor must not be null.");
+        }
+
+        synchronized (mInputDevicesLock) {
+            populateInputDevicesLocked();
+
+            int numDevices = mInputDevices.size();
+            for (int i = 0; i < numDevices; i++) {
+                InputDevice inputDevice = mInputDevices.valueAt(i);
+                if (inputDevice == null) {
+                    int id = mInputDevices.keyAt(i);
+                    try {
+                        inputDevice = mIm.getInputDevice(id);
+                    } catch (RemoteException ex) {
+                        // Ignore the problem for the purposes of this method.
+                        continue;
+                    }
+                    mInputDevices.setValueAt(i, inputDevice);
+                }
+                if (descriptor.equals(inputDevice.getDescriptor())) {
+                    return inputDevice;
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
      * Gets the ids of all input devices in the system.
      * @return The input device ids.
      */
@@ -332,50 +369,129 @@
     }
 
     /**
-     * Gets the keyboard layout descriptor for the specified input device.
+     * Gets the current keyboard layout descriptor for the specified input device.
      *
      * @param inputDeviceDescriptor The input device descriptor.
-     * @return The keyboard layout descriptor, or null if unknown or if the default
-     * keyboard layout will be used.
+     * @return The keyboard layout descriptor, or null if no keyboard layout has been set.
      *
      * @hide
      */
-    public String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
+    public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
         if (inputDeviceDescriptor == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
 
         try {
-            return mIm.getKeyboardLayoutForInputDevice(inputDeviceDescriptor);
+            return mIm.getCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor);
         } catch (RemoteException ex) {
-            Log.w(TAG, "Could not get keyboard layout for input device.", ex);
+            Log.w(TAG, "Could not get current keyboard layout for input device.", ex);
             return null;
         }
     }
 
     /**
-     * Sets the keyboard layout descriptor for the specified input device.
+     * Sets the current keyboard layout descriptor for the specified input device.
      * <p>
      * This method may have the side-effect of causing the input device in question
      * to be reconfigured.
      * </p>
      *
      * @param inputDeviceDescriptor The input device descriptor.
-     * @param keyboardLayoutDescriptor The keyboard layout descriptor, or null to remove
-     * the mapping so that the default keyboard layout will be used for the input device.
+     * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null.
      *
      * @hide
      */
-    public void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
             String keyboardLayoutDescriptor) {
         if (inputDeviceDescriptor == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
+
+        try {
+            mIm.setCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor,
+                    keyboardLayoutDescriptor);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not set current keyboard layout for input device.", ex);
+        }
+    }
+
+    /**
+     * Gets all keyboard layout descriptors that are enabled for the specified input device.
+     *
+     * @param inputDeviceDescriptor The input device descriptor.
+     * @return The keyboard layout descriptors.
+     *
+     * @hide
+     */
+    public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) {
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
 
         try {
-            mIm.setKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor);
+            return mIm.getKeyboardLayoutsForInputDevice(inputDeviceDescriptor);
         } catch (RemoteException ex) {
-            Log.w(TAG, "Could not set keyboard layout for input device.", ex);
+            Log.w(TAG, "Could not get keyboard layouts for input device.", ex);
+            return ArrayUtils.emptyArray(String.class);
+        }
+    }
+
+    /**
+     * Adds the keyboard layout descriptor for the specified input device.
+     * <p>
+     * This method may have the side-effect of causing the input device in question
+     * to be reconfigured.
+     * </p>
+     *
+     * @param inputDeviceDescriptor The input device descriptor.
+     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to add.
+     *
+     * @hide
+     */
+    public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
+
+        try {
+            mIm.addKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not add keyboard layout for input device.", ex);
+        }
+    }
+
+    /**
+     * Removes the keyboard layout descriptor for the specified input device.
+     * <p>
+     * This method may have the side-effect of causing the input device in question
+     * to be reconfigured.
+     * </p>
+     *
+     * @param inputDeviceDescriptor The input device descriptor.
+     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to remove.
+     *
+     * @hide
+     */
+    public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
+
+        try {
+            mIm.removeKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not remove keyboard layout for input device.", ex);
         }
     }
 
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 97ed235..69e1de9 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -36,6 +36,13 @@
  * perform background operations and publish results on the UI thread without
  * having to manipulate threads and/or handlers.</p>
  *
+ * <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
+ * and does not constitute a generic threading framework. AsyncTasks should ideally be
+ * used for short operations (a few seconds at the most.) If you need to keep threads
+ * running for long periods of time, it is highly recommended you use the various APIs
+ * provided by the <code>java.util.concurrent</code> pacakge such as {@link Executor},
+ * {@link ThreadPoolExecutor} and {@link FutureTask}.</p>
+ *
  * <p>An asynchronous task is defined by a computation that runs on a background thread and
  * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
  * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
@@ -63,6 +70,8 @@
  *         for (int i = 0; i < count; i++) {
  *             totalSize += Downloader.downloadFile(urls[i]);
  *             publishProgress((int) ((i / (float) count) * 100));
+ *             // Escape early if cancel() is called
+ *             if (isCancelled()) break;
  *         }
  *         return totalSize;
  *     }
@@ -154,6 +163,16 @@
  *     <li>Set member fields in {@link #doInBackground}, and refer to them in
  *     {@link #onProgressUpdate} and {@link #onPostExecute}.
  * </ul>
+ *
+ * <h2>Order of execution</h2>
+ * <p>When first introduced, AsyncTasks were executed serially on a single background
+ * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
+ * to a pool of threads allowing multiple tasks to operate in parallel. Starting with
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
+ * thread to avoid common application errors caused by parallel execution.</p>
+ * <p>If you truly want parallel execution, you can invoke
+ * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
+ * {@link #THREAD_POOL_EXECUTOR}.</p>
  */
 public abstract class AsyncTask<Params, Progress, Result> {
     private static final String LOG_TAG = "AsyncTask";
@@ -491,13 +510,13 @@
      * thread or pool of threads depending on the platform version.  When first
      * introduced, AsyncTasks were executed serially on a single background thread.
      * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
-     * to a pool of threads allowing multiple tasks to operate in parallel.  After
-     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, it is planned to change this
-     * back to a single thread to avoid common application errors caused
+     * to a pool of threads allowing multiple tasks to operate in parallel. Starting
+     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
+     * executed on a single thread to avoid common application errors caused
      * by parallel execution.  If you truly want parallel execution, you can use
      * the {@link #executeOnExecutor} version of this method
-     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings on
-     * its use.
+     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
+     * on its use.
      *
      * <p>This method must be invoked on the UI thread.
      *
@@ -507,6 +526,9 @@
      *
      * @throws IllegalStateException If {@link #getStatus()} returns either
      *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+     *
+     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
+     * @see #execute(Runnable)
      */
     public final AsyncTask<Params, Progress, Result> execute(Params... params) {
         return executeOnExecutor(sDefaultExecutor, params);
@@ -542,6 +564,8 @@
      *
      * @throws IllegalStateException If {@link #getStatus()} returns either
      *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+     *
+     * @see #execute(Object[])
      */
     public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
             Params... params) {
@@ -569,7 +593,11 @@
 
     /**
      * Convenience version of {@link #execute(Object...)} for use with
-     * a simple Runnable object.
+     * a simple Runnable object. See {@link #execute(Object[])} for more
+     * information on the order of execution.
+     *
+     * @see #execute(Object[])
+     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
      */
     public static void execute(Runnable runnable) {
         sDefaultExecutor.execute(runnable);
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index a06aadb6..02135bc 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -127,29 +127,17 @@
                 return;
             }
 
-            long wallStart = 0;
-            long threadStart = 0;
-
             // This must be in a local variable, in case a UI event sets the logger
             Printer logging = me.mLogging;
             if (logging != null) {
                 logging.println(">>>>> Dispatching to " + msg.target + " " +
                         msg.callback + ": " + msg.what);
-                wallStart = SystemClock.currentTimeMicro();
-                threadStart = SystemClock.currentThreadTimeMicro();
             }
 
             msg.target.dispatchMessage(msg);
 
             if (logging != null) {
-                long wallTime = SystemClock.currentTimeMicro() - wallStart;
-                long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
-
                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
-                if (logging instanceof Profiler) {
-                    ((Profiler) logging).profile(msg, wallStart, wallTime,
-                            threadStart, threadTime);
-                }
             }
 
             // Make sure that during the course of dispatching the
@@ -290,12 +278,4 @@
     public String toString() {
         return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}";
     }
-
-    /**
-     * @hide
-     */
-    public static interface Profiler {
-        void profile(Message message, long wallStart, long wallTime,
-                long threadStart, long threadTime);
-    }
 }
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 25d08ac..0114a41 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -585,8 +585,12 @@
             }
             // Hold the event we obtained above - listeners may have changed the original.
             mPreviousUpEvent = currentUpEvent;
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
+            if (mVelocityTracker != null) {
+                // This may have been cleared when we called out to the
+                // application above.
+                mVelocityTracker.recycle();
+                mVelocityTracker = null;
+            }
             mIsDoubleTapping = false;
             mHandler.removeMessages(SHOW_PRESS);
             mHandler.removeMessages(LONG_PRESS);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index e25e2ef..f986d15 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -647,15 +647,10 @@
                 Log.d(LOG_TAG, "Disabling v-sync");
             }
 
-            //noinspection PointlessBooleanExpression,ConstantConditions
-            if (!ViewDebug.DEBUG_LATENCY) {
-                property = SystemProperties.get(PROFILE_PROPERTY, "false");
-                mProfileEnabled = "true".equalsIgnoreCase(property);
-                if (mProfileEnabled) {
-                    Log.d(LOG_TAG, "Profiling hardware renderer");
-                }
-            } else {
-                mProfileEnabled = true;
+            property = SystemProperties.get(PROFILE_PROPERTY, "false");
+            mProfileEnabled = "true".equalsIgnoreCase(property);
+            if (mProfileEnabled) {
+                Log.d(LOG_TAG, "Profiling hardware renderer");
             }
 
             if (mProfileEnabled) {
@@ -1132,11 +1127,6 @@
                             float total = (now - getDisplayListStartTime) * 0.000001f;
                             //noinspection PointlessArithmeticExpression
                             mProfileData[mProfileCurrentFrame] = total;
-
-                            if (ViewDebug.DEBUG_LATENCY) {
-                                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- getDisplayList() took " +
-                                        total + "ms");
-                            }
                         }
 
                         if (displayList != null) {
@@ -1152,11 +1142,6 @@
                                 long now = System.nanoTime();
                                 float total = (now - drawDisplayListStartTime) * 0.000001f;
                                 mProfileData[mProfileCurrentFrame + 1] = total;
-
-                                if (ViewDebug.DEBUG_LATENCY) {
-                                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- drawDisplayList() took " +
-                                            total + "ms, status=" + status);
-                                }
                             }
 
                             handleFunctorStatus(attachInfo, status);
@@ -1198,11 +1183,6 @@
                         long now = System.nanoTime();
                         float total = (now - eglSwapBuffersStartTime) * 0.000001f;
                         mProfileData[mProfileCurrentFrame + 2] = total;
-
-                        if (ViewDebug.DEBUG_LATENCY) {
-                            Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- eglSwapBuffers() took " +
-                                    total + "ms");
-                        }
                     }
 
                     checkEglErrors();
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index c46e43a..a7ee12b 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -65,6 +65,7 @@
     private final Object[] mActionProviderConstructorArguments;
 
     private Context mContext;
+    private Object mRealOwner;
 
     /**
      * Constructs a menu inflater.
@@ -73,6 +74,20 @@
      */
     public MenuInflater(Context context) {
         mContext = context;
+        mRealOwner = context;
+        mActionViewConstructorArguments = new Object[] {context};
+        mActionProviderConstructorArguments = mActionViewConstructorArguments;
+    }
+
+    /**
+     * Constructs a menu inflater.
+     *
+     * @see Activity#getMenuInflater()
+     * @hide
+     */
+    public MenuInflater(Context context, Object realOwner) {
+        mContext = context;
+        mRealOwner = realOwner;
         mActionViewConstructorArguments = new Object[] {context};
         mActionProviderConstructorArguments = mActionViewConstructorArguments;
     }
@@ -190,12 +205,12 @@
             implements MenuItem.OnMenuItemClickListener {
         private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class };
         
-        private Context mContext;
+        private Object mRealOwner;
         private Method mMethod;
         
-        public InflatedOnMenuItemClickListener(Context context, String methodName) {
-            mContext = context;
-            Class<?> c = context.getClass();
+        public InflatedOnMenuItemClickListener(Object realOwner, String methodName) {
+            mRealOwner = realOwner;
+            Class<?> c = realOwner.getClass();
             try {
                 mMethod = c.getMethod(methodName, PARAM_TYPES);
             } catch (Exception e) {
@@ -210,9 +225,9 @@
         public boolean onMenuItemClick(MenuItem item) {
             try {
                 if (mMethod.getReturnType() == Boolean.TYPE) {
-                    return (Boolean) mMethod.invoke(mContext, item);
+                    return (Boolean) mMethod.invoke(mRealOwner, item);
                 } else {
-                    mMethod.invoke(mContext, item);
+                    mMethod.invoke(mRealOwner, item);
                     return true;
                 }
             } catch (Exception e) {
@@ -400,7 +415,7 @@
                             + "be used within a restricted context");
                 }
                 item.setOnMenuItemClickListener(
-                        new InflatedOnMenuItemClickListener(mContext, itemListenerMethodName));
+                        new InflatedOnMenuItemClickListener(mRealOwner, itemListenerMethodName));
             }
 
             if (item instanceof MenuItemImpl) {
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 411aed3..a719a01 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -374,6 +374,14 @@
             // tell mLayer about it and set the SurfaceTexture to use the
             // current view size.
             mUpdateSurface = false;
+
+            // Since we are updating the layer, force an update to ensure its
+            // parameters are correct (width, height, transform, etc.)
+            synchronized (mLock) {
+                mUpdateLayer = true;
+            }
+            mMatrixChanged = true;
+
             mAttachInfo.mHardwareRenderer.setSurfaceTexture(mLayer, mSurface);
             nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
         }
@@ -471,7 +479,7 @@
     }
 
     private void applyTransformMatrix() {
-        if (mMatrixChanged) {
+        if (mMatrixChanged && mLayer != null) {
             mLayer.setTransform(mMatrix);
             mMatrixChanged = false;
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6d60797..832d575 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4325,7 +4325,6 @@
         if (gainFocus) {
             if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-                requestAccessibilityFocus();
             }
         }
 
@@ -6183,8 +6182,6 @@
             invalidate();
             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
             notifyAccessibilityStateChanged();
-            // Try to give input focus to this view - not a descendant.
-            requestFocusNoSearch(View.FOCUS_DOWN, null);
             return true;
         }
         return false;
@@ -6215,26 +6212,19 @@
 
             // Clear the text navigation state.
             setAccessibilityCursorPosition(-1);
-
-            // Try to move accessibility focus to the input focus.
-            View rootView = getRootView();
-            if (rootView != null) {
-                View inputFocus = rootView.findFocus();
-                if (inputFocus != null) {
-                    inputFocus.requestAccessibilityFocus();
-                }
-            }
         }
     }
 
     private void requestAccessibilityFocusFromHover() {
         if (includeForAccessibility() && isActionableForAccessibility()) {
             requestAccessibilityFocus();
+            requestFocusNoSearch(View.FOCUS_DOWN, null);
         } else {
             if (mParent != null) {
                 View nextFocus = mParent.findViewToTakeAccessibilityFocusFromHover(this, this);
                 if (nextFocus != null) {
                     nextFocus.requestAccessibilityFocus();
+                    nextFocus.requestFocusNoSearch(View.FOCUS_DOWN, null);
                 }
             }
         }
@@ -9912,10 +9902,6 @@
      * @param dirty the rectangle representing the bounds of the dirty region
      */
     public void invalidate(Rect dirty) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
-        }
-
         if (skipInvalidate()) {
             return;
         }
@@ -9959,10 +9945,6 @@
      * @param b the bottom position of the dirty region
      */
     public void invalidate(int l, int t, int r, int b) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
-        }
-
         if (skipInvalidate()) {
             return;
         }
@@ -10015,10 +9997,6 @@
      * View's contents or dimensions have not changed.
      */
     void invalidate(boolean invalidateCache) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
-        }
-
         if (skipInvalidate()) {
             return;
         }
@@ -12391,10 +12369,6 @@
                 mDrawingCache == null : mUnscaledDrawingCache == null)) {
             mCachingFailed = false;
 
-            if (ViewDebug.TRACE_HIERARCHY) {
-                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
-            }
-
             int width = mRight - mLeft;
             int height = mBottom - mTop;
 
@@ -12514,9 +12488,6 @@
 
             // Fast path for layouts with no backgrounds
             if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
-                if (ViewDebug.TRACE_HIERARCHY) {
-                    ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
-                }
                 mPrivateFlags &= ~DIRTY_MASK;
                 dispatchDraw(canvas);
             } else {
@@ -13130,9 +13101,6 @@
                 if (!hasDisplayList) {
                     // Fast path for layouts with no backgrounds
                     if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
-                        if (ViewDebug.TRACE_HIERARCHY) {
-                            ViewDebug.trace(parent, ViewDebug.HierarchyTraceType.DRAW);
-                        }
                         mPrivateFlags &= ~DIRTY_MASK;
                         dispatchDraw(canvas);
                     } else {
@@ -13205,10 +13173,6 @@
      * @param canvas The Canvas to which the View is rendered.
      */
     public void draw(Canvas canvas) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
-        }
-
         final int privateFlags = mPrivateFlags;
         final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
                 (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
@@ -13552,10 +13516,6 @@
         int oldR = mRight;
         boolean changed = setFrame(l, t, r, b);
         if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
-            if (ViewDebug.TRACE_HIERARCHY) {
-                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
-            }
-
             onLayout(changed, l, t, r, b);
             mPrivateFlags &= ~LAYOUT_REQUIRED;
 
@@ -14811,60 +14771,6 @@
     }
 
     /**
-     * @param consistency The type of consistency. See ViewDebug for more information.
-     *
-     * @hide
-     */
-    protected boolean dispatchConsistencyCheck(int consistency) {
-        return onConsistencyCheck(consistency);
-    }
-
-    /**
-     * Method that subclasses should implement to check their consistency. The type of
-     * consistency check is indicated by the bit field passed as a parameter.
-     *
-     * @param consistency The type of consistency. See ViewDebug for more information.
-     *
-     * @throws IllegalStateException if the view is in an inconsistent state.
-     *
-     * @hide
-     */
-    protected boolean onConsistencyCheck(int consistency) {
-        boolean result = true;
-
-        final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
-        final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
-
-        if (checkLayout) {
-            if (getParent() == null) {
-                result = false;
-                android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                        "View " + this + " does not have a parent.");
-            }
-
-            if (mAttachInfo == null) {
-                result = false;
-                android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                        "View " + this + " is not attached to a window.");
-            }
-        }
-
-        if (checkDrawing) {
-            // Do not check the DIRTY/DRAWN flags because views can call invalidate()
-            // from their draw() method
-
-            if ((mPrivateFlags & DRAWN) != DRAWN &&
-                    (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
-                result = false;
-                android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                        "View " + this + " was invalidated but its drawing cache is valid.");
-            }
-        }
-
-        return result;
-    }
-
-    /**
      * Prints information about this view in the log output, with the tag
      * {@link #VIEW_LOG_TAG}.
      *
@@ -14977,10 +14883,6 @@
      * tree.
      */
     public void requestLayout() {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);
-        }
-
         mPrivateFlags |= FORCE_LAYOUT;
         mPrivateFlags |= INVALIDATED;
 
@@ -15031,10 +14933,6 @@
             // first clears the measured dimension flag
             mPrivateFlags &= ~MEASURED_DIMENSION_SET;
 
-            if (ViewDebug.TRACE_HIERARCHY) {
-                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
-            }
-
             // measure ourselves, this should set the measured dimension flag back
             onMeasure(widthMeasureSpec, heightMeasureSpec);
 
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 9134966..823befb 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -193,19 +193,12 @@
     private static final int MAXIMUM_FLING_VELOCITY = 8000;
 
     /**
-     * Distance in dips between a touch up event denoting the end of a touch exploration
-     * gesture and the touch up event of a subsequent tap for the latter tap to be
-     * considered as a tap i.e. to perform a click.
-     */
-    private static final int TOUCH_EXPLORE_TAP_SLOP = 80;
-
-    /**
      * Delay before dispatching a recurring accessibility event in milliseconds.
      * This delay guarantees that a recurring event will be send at most once
      * during the {@link #SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS} time
      * frame.
      */
-    private static final long SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS = 400;
+    private static final long SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS = 100;
 
     /**
      * The maximum size of View's drawing cache, expressed in bytes. This size
@@ -238,7 +231,6 @@
     private final int mDoubleTapTouchSlop;
     private final int mPagingTouchSlop;
     private final int mDoubleTapSlop;
-    private final int mScaledTouchExploreTapSlop;
     private final int mWindowTouchSlop;
     private final int mMaximumDrawingCacheSize;
     private final int mOverscrollDistance;
@@ -265,7 +257,6 @@
         mDoubleTapTouchSlop = DOUBLE_TAP_TOUCH_SLOP;
         mPagingTouchSlop = PAGING_TOUCH_SLOP;
         mDoubleTapSlop = DOUBLE_TAP_SLOP;
-        mScaledTouchExploreTapSlop = TOUCH_EXPLORE_TAP_SLOP;
         mWindowTouchSlop = WINDOW_TOUCH_SLOP;
         //noinspection deprecation
         mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
@@ -302,7 +293,6 @@
         mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f);
         mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
         mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f);
-        mScaledTouchExploreTapSlop = (int) (density * TOUCH_EXPLORE_TAP_SLOP + 0.5f);
         mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
 
         final Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
@@ -553,17 +543,6 @@
     }
 
     /**
-     * @return Distance in pixels between a touch up event denoting the end of a touch exploration
-     * gesture and the touch up event of a subsequent tap for the latter tap to be
-     * considered as a tap i.e. to perform a click.
-     *
-     * @hide
-     */
-    public int getScaledTouchExploreTapSlop() {
-        return mScaledTouchExploreTapSlop;
-    }
-
-    /**
      * Interval for dispatching a recurring accessibility event in milliseconds.
      * This interval guarantees that a recurring event will be send at most once
      * during the {@link #getSendRecurringAccessibilityEventsInterval()} time frame.
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index cb37a1c..dd671dc 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -22,24 +22,14 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.os.Debug;
-import android.os.Environment;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.Printer;
 
 import java.io.BufferedOutputStream;
 import java.io.BufferedWriter;
 import java.io.ByteArrayOutputStream;
 import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
@@ -51,14 +41,8 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -67,107 +51,24 @@
  */
 public class ViewDebug {
     /**
-     * Log tag used to log errors related to the consistency of the view hierarchy.
-     *
-     * @hide
+     * @deprecated This flag is now unused
      */
-    public static final String CONSISTENCY_LOG_TAG = "ViewConsistency";
-
-    /**
-     * Flag indicating the consistency check should check layout-related properties.
-     *
-     * @hide
-     */
-    public static final int CONSISTENCY_LAYOUT = 0x1;
-
-    /**
-     * Flag indicating the consistency check should check drawing-related properties.
-     *
-     * @hide
-     */
-    public static final int CONSISTENCY_DRAWING = 0x2;
-
-    /**
-     * Enables or disables view hierarchy tracing. Any invoker of
-     * {@link #trace(View, android.view.ViewDebug.HierarchyTraceType)} should first
-     * check that this value is set to true as not to affect performance.
-     */
+    @Deprecated
     public static final boolean TRACE_HIERARCHY = false;
 
     /**
-     * Enables or disables view recycler tracing. Any invoker of
-     * {@link #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])} should first
-     * check that this value is set to true as not to affect performance.
+     * @deprecated This flag is now unused
      */
+    @Deprecated
     public static final boolean TRACE_RECYCLER = false;
 
     /**
-     * Profiles drawing times in the events log.
-     *
-     * @hide
-     */
-    public static final boolean DEBUG_PROFILE_DRAWING = false;
-
-    /**
-     * Profiles layout times in the events log.
-     *
-     * @hide
-     */
-    public static final boolean DEBUG_PROFILE_LAYOUT = false;
-
-    /**
      * Enables detailed logging of drag/drop operations.
      * @hide
      */
     public static final boolean DEBUG_DRAG = false;
 
     /**
-     * Enables logging of factors that affect the latency and responsiveness of an application.
-     *
-     * Logs the relative difference between the time an event was created and the time it
-     * was delivered.
-     *
-     * Logs the time spent waiting for Surface.lockCanvas(), Surface.unlockCanvasAndPost()
-     * or eglSwapBuffers().  This is time that the event loop spends blocked and unresponsive.
-     * Ideally, drawing and animations should be perfectly synchronized with VSYNC so that
-     * dequeuing and queueing buffers is instantaneous.
-     *
-     * Logs the time spent in ViewRoot.performTraversals() and ViewRoot.performDraw().
-     * @hide
-     */
-    public static final boolean DEBUG_LATENCY = false;
-
-    /** @hide */
-    public static final String DEBUG_LATENCY_TAG = "ViewLatency";
-
-    /**
-     * Enables detailed logging of accessibility focus operations.
-     * @hide
-     */
-    public static final boolean DEBUG_ACCESSIBILITY_FOCUS = false;
-
-    /**
-     * Tag for logging of accessibility focus operations
-     * @hide
-     */
-    public static final String DEBUG_ACCESSIBILITY_FOCUS_TAG = "AccessibilityFocus";
-
-    /**
-     * <p>Enables or disables views consistency check. Even when this property is enabled,
-     * view consistency checks happen only if {@link false} is set
-     * to true. The value of this property can be configured externally in one of the
-     * following files:</p>
-     * <ul>
-     *  <li>/system/debug.prop</li>
-     *  <li>/debug.prop</li>
-     *  <li>/data/debug.prop</li>
-     * </ul>
-     * @hide
-     */
-    @Debug.DebugProperty
-    public static boolean consistencyCheckEnabled = false;
-
-    /**
      * This annotation can be used to mark fields and methods to be dumped by
      * the view server. Only non-void methods with no arguments can be annotated
      * by this annotation.
@@ -373,8 +274,9 @@
     private static HashMap<AccessibleObject, ExportedProperty> sAnnotations;
 
     /**
-     * Defines the type of hierarhcy trace to output to the hierarchy traces file.
+     * @deprecated This enum is now unused
      */
+    @Deprecated
     public enum HierarchyTraceType {
         INVALIDATE,
         INVALIDATE_CHILD,
@@ -386,13 +288,10 @@
         BUILD_CACHE
     }
 
-    private static BufferedWriter sHierarchyTraces;
-    private static ViewRootImpl sHierarchyRoot;
-    private static String sHierarchyTracePrefix;
-
     /**
-     * Defines the type of recycler trace to output to the recycler traces file.
+     * @deprecated This enum is now unused
      */
+    @Deprecated
     public enum RecyclerTraceType {
         NEW_VIEW,
         BIND_VIEW,
@@ -402,21 +301,6 @@
         MOVE_FROM_ACTIVE_TO_SCRAP_HEAP
     }
 
-    private static class RecyclerTrace {
-        public int view;
-        public RecyclerTraceType type;
-        public int position;
-        public int indexOnScreen;
-    }
-
-    private static View sRecyclerOwnerView;
-    private static List<View> sRecyclerViews;
-    private static List<RecyclerTrace> sRecyclerTraces;
-    private static String sRecyclerTracePrefix;
-
-    private static final ThreadLocal<LooperProfiler> sLooperProfilerStorage =
-            new ThreadLocal<LooperProfiler>();
-
     /**
      * Returns the number of instanciated Views.
      *
@@ -440,511 +324,50 @@
     }
 
     /**
-     * Starts profiling the looper associated with the current thread.
-     * You must call {@link #stopLooperProfiling} to end profiling
-     * and obtain the traces. Both methods must be invoked on the
-     * same thread.
-     * 
-     * @hide
+     * @deprecated This method is now unused and invoking it is a no-op
      */
-    public static void startLooperProfiling(String path, FileDescriptor fileDescriptor) {
-        if (sLooperProfilerStorage.get() == null) {
-            LooperProfiler profiler = new LooperProfiler(path, fileDescriptor);
-            sLooperProfilerStorage.set(profiler);
-            Looper.myLooper().setMessageLogging(profiler);
-        }
-    }    
-
-    /**
-     * Stops profiling the looper associated with the current thread.
-     * 
-     * @see #startLooperProfiling(String, java.io.FileDescriptor) 
-     * 
-     * @hide
-     */
-    public static void stopLooperProfiling() {
-        LooperProfiler profiler = sLooperProfilerStorage.get();
-        if (profiler != null) {
-            sLooperProfilerStorage.remove();
-            Looper.myLooper().setMessageLogging(null);
-            profiler.save();
-        }
-    }
-
-    private static class LooperProfiler implements Looper.Profiler, Printer {
-        private static final String LOG_TAG = "LooperProfiler";
-
-        private static final int TRACE_VERSION_NUMBER = 3;
-        private static final int ACTION_EXIT_METHOD = 0x1;
-        private static final int HEADER_SIZE = 32;
-        private static final String HEADER_MAGIC = "SLOW";
-        private static final short HEADER_RECORD_SIZE = (short) 14;
-
-        private final long mTraceWallStart;
-        private final long mTraceThreadStart;
-        
-        private final ArrayList<Entry> mTraces = new ArrayList<Entry>(512);
-
-        private final HashMap<String, Integer> mTraceNames = new HashMap<String, Integer>(32);
-        private int mTraceId = 0;
-
-        private final String mPath;
-        private ParcelFileDescriptor mFileDescriptor;
-
-        LooperProfiler(String path, FileDescriptor fileDescriptor) {
-            mPath = path;
-            try {
-                mFileDescriptor = ParcelFileDescriptor.dup(fileDescriptor);
-            } catch (IOException e) {
-                Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
-                throw new RuntimeException(e);
-            }
-            mTraceWallStart = SystemClock.currentTimeMicro();
-            mTraceThreadStart = SystemClock.currentThreadTimeMicro();            
-        }
-
-        @Override
-        public void println(String x) {
-            // Ignore messages
-        }
-
-        @Override
-        public void profile(Message message, long wallStart, long wallTime,
-                long threadStart, long threadTime) {
-            Entry entry = new Entry();
-            entry.traceId = getTraceId(message);
-            entry.wallStart = wallStart;
-            entry.wallTime = wallTime;
-            entry.threadStart = threadStart;
-            entry.threadTime = threadTime;
-
-            mTraces.add(entry);
-        }
-
-        private int getTraceId(Message message) {
-            String name = message.getTarget().getMessageName(message);
-            Integer traceId = mTraceNames.get(name);
-            if (traceId == null) {
-                traceId = mTraceId++ << 4;
-                mTraceNames.put(name, traceId);
-            }
-            return traceId;
-        }
-
-        void save() {
-            // Don't block the UI thread
-            new Thread(new Runnable() {
-                @Override
-                public void run() {
-                    saveTraces();
-                }
-            }, "LooperProfiler[" + mPath + "]").start();
-        }
-
-        private void saveTraces() {
-            FileOutputStream fos = new FileOutputStream(mFileDescriptor.getFileDescriptor());
-            DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
-
-            try {
-                writeHeader(out, mTraceWallStart, mTraceNames, mTraces);
-                out.flush();
-
-                writeTraces(fos, out.size(), mTraceWallStart, mTraceThreadStart, mTraces);
-
-                Log.d(LOG_TAG, "Looper traces ready: " + mPath);
-            } catch (IOException e) {
-                Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
-            } finally {
-                try {
-                    out.close();
-                } catch (IOException e) {
-                    Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
-                }
-                try {
-                    mFileDescriptor.close();
-                } catch (IOException e) {
-                    Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
-                }
-            }
-        }
-        
-        private static void writeTraces(FileOutputStream out, long offset, long wallStart,
-                long threadStart, ArrayList<Entry> entries) throws IOException {
-    
-            FileChannel channel = out.getChannel();
-    
-            // Header
-            ByteBuffer buffer = ByteBuffer.allocateDirect(HEADER_SIZE);
-            buffer.put(HEADER_MAGIC.getBytes());
-            buffer = buffer.order(ByteOrder.LITTLE_ENDIAN);
-            buffer.putShort((short) TRACE_VERSION_NUMBER);    // version
-            buffer.putShort((short) HEADER_SIZE);             // offset to data
-            buffer.putLong(wallStart);                        // start time in usec
-            buffer.putShort(HEADER_RECORD_SIZE);              // size of a record in bytes
-            // padding to 32 bytes
-            for (int i = 0; i < HEADER_SIZE - 18; i++) {
-                buffer.put((byte) 0);
-            }
-    
-            buffer.flip();
-            channel.position(offset).write(buffer);
-            
-            buffer = ByteBuffer.allocateDirect(14).order(ByteOrder.LITTLE_ENDIAN);
-            for (Entry entry : entries) {
-                buffer.putShort((short) 1);   // we simulate only one thread
-                buffer.putInt(entry.traceId); // entering method
-                buffer.putInt((int) (entry.threadStart - threadStart));
-                buffer.putInt((int) (entry.wallStart - wallStart));
-    
-                buffer.flip();
-                channel.write(buffer);
-                buffer.clear();
-    
-                buffer.putShort((short) 1);
-                buffer.putInt(entry.traceId | ACTION_EXIT_METHOD); // exiting method
-                buffer.putInt((int) (entry.threadStart + entry.threadTime - threadStart));
-                buffer.putInt((int) (entry.wallStart + entry.wallTime - wallStart));
-    
-                buffer.flip();
-                channel.write(buffer);
-                buffer.clear();
-            }
-    
-            channel.close();
-        }
-    
-        private static void writeHeader(DataOutputStream out, long start,
-                HashMap<String, Integer> names, ArrayList<Entry> entries) throws IOException {
-    
-            Entry last = entries.get(entries.size() - 1);
-            long wallTotal = (last.wallStart + last.wallTime) - start;
-    
-            startSection("version", out);
-            addValue(null, Integer.toString(TRACE_VERSION_NUMBER), out);
-            addValue("data-file-overflow", "false", out);
-            addValue("clock", "dual", out);
-            addValue("elapsed-time-usec", Long.toString(wallTotal), out);
-            addValue("num-method-calls", Integer.toString(entries.size()), out);
-            addValue("clock-call-overhead-nsec", "1", out);
-            addValue("vm", "dalvik", out);
-    
-            startSection("threads", out);
-            addThreadId(1, "main", out);
-    
-            startSection("methods", out);
-            addMethods(names, out);
-    
-            startSection("end", out);
-        }
-    
-        private static void addMethods(HashMap<String, Integer> names, DataOutputStream out)
-                throws IOException {
-    
-            for (Map.Entry<String, Integer> name : names.entrySet()) {
-                out.writeBytes(String.format("0x%08x\tEventQueue\t%s\t()V\tLooper\t-2\n",
-                        name.getValue(), name.getKey()));
-            }
-        }
-    
-        private static void addThreadId(int id, String name, DataOutputStream out)
-                throws IOException {
-
-            out.writeBytes(Integer.toString(id) + '\t' + name + '\n');
-        }
-    
-        private static void addValue(String name, String value, DataOutputStream out)
-                throws IOException {
-    
-            if (name != null) {
-                out.writeBytes(name + "=");
-            }
-            out.writeBytes(value + '\n');
-        }
-    
-        private static void startSection(String name, DataOutputStream out) throws IOException {
-            out.writeBytes("*" + name + '\n');
-        }
-
-        static class Entry {
-            int traceId;
-            long wallStart;
-            long wallTime;
-            long threadStart;
-            long threadTime;
-        }
-    }
-
-    /**
-     * Outputs a trace to the currently opened recycler traces. The trace records the type of
-     * recycler action performed on the supplied view as well as a number of parameters.
-     *
-     * @param view the view to trace
-     * @param type the type of the trace
-     * @param parameters parameters depending on the type of the trace
-     */
+    @Deprecated
+    @SuppressWarnings({ "UnusedParameters", "deprecation" })
     public static void trace(View view, RecyclerTraceType type, int... parameters) {
-        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
-            return;
-        }
-
-        if (!sRecyclerViews.contains(view)) {
-            sRecyclerViews.add(view);
-        }
-
-        final int index = sRecyclerViews.indexOf(view);
-
-        RecyclerTrace trace = new RecyclerTrace();
-        trace.view = index;
-        trace.type = type;
-        trace.position = parameters[0];
-        trace.indexOnScreen = parameters[1];
-
-        sRecyclerTraces.add(trace);
     }
 
     /**
-     * Starts tracing the view recycler of the specified view. The trace is identified by a prefix,
-     * used to build the traces files names: <code>/EXTERNAL/view-recycler/PREFIX.traces</code> and
-     * <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>.
-     *
-     * Only one view recycler can be traced at the same time. After calling this method, any
-     * other invocation will result in a <code>IllegalStateException</code> unless
-     * {@link #stopRecyclerTracing()} is invoked before.
-     *
-     * Traces files are created only after {@link #stopRecyclerTracing()} is invoked.
-     *
-     * This method will return immediately if TRACE_RECYCLER is false.
-     *
-     * @param prefix the traces files name prefix
-     * @param view the view whose recycler must be traced
-     *
-     * @see #stopRecyclerTracing()
-     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
+     * @deprecated This method is now unused and invoking it is a no-op
      */
+    @Deprecated
+    @SuppressWarnings("UnusedParameters")
     public static void startRecyclerTracing(String prefix, View view) {
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (!TRACE_RECYCLER) {
-            return;
-        }
-
-        if (sRecyclerOwnerView != null) {
-            throw new IllegalStateException("You must call stopRecyclerTracing() before running" +
-                " a new trace!");
-        }
-
-        sRecyclerTracePrefix = prefix;
-        sRecyclerOwnerView = view;
-        sRecyclerViews = new ArrayList<View>();
-        sRecyclerTraces = new LinkedList<RecyclerTrace>();
     }
 
     /**
-     * Stops the current view recycer tracing.
-     *
-     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.traces</code>
-     * containing all the traces (or method calls) relative to the specified view's recycler.
-     *
-     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>
-     * containing all of the views used by the recycler of the view supplied to
-     * {@link #startRecyclerTracing(String, View)}.
-     *
-     * This method will return immediately if TRACE_RECYCLER is false.
-     *
-     * @see #startRecyclerTracing(String, View)
-     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
+     * @deprecated This method is now unused and invoking it is a no-op
      */
+    @Deprecated
+    @SuppressWarnings("UnusedParameters")
     public static void stopRecyclerTracing() {
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (!TRACE_RECYCLER) {
-            return;
-        }
-
-        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
-            throw new IllegalStateException("You must call startRecyclerTracing() before" +
-                " stopRecyclerTracing()!");
-        }
-
-        File recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
-        //noinspection ResultOfMethodCallIgnored
-        recyclerDump.mkdirs();
-
-        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".recycler");
-        try {
-            final BufferedWriter out = new BufferedWriter(new FileWriter(recyclerDump), 8 * 1024);
-
-            for (View view : sRecyclerViews) {
-                final String name = view.getClass().getName();
-                out.write(name);
-                out.newLine();
-            }
-
-            out.close();
-        } catch (IOException e) {
-            Log.e("View", "Could not dump recycler content");
-            return;
-        }
-
-        recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
-        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces");
-        try {
-            if (recyclerDump.exists()) {
-                //noinspection ResultOfMethodCallIgnored
-                recyclerDump.delete();
-            }
-            final FileOutputStream file = new FileOutputStream(recyclerDump);
-            final DataOutputStream out = new DataOutputStream(file);
-
-            for (RecyclerTrace trace : sRecyclerTraces) {
-                out.writeInt(trace.view);
-                out.writeInt(trace.type.ordinal());
-                out.writeInt(trace.position);
-                out.writeInt(trace.indexOnScreen);
-                out.flush();
-            }
-
-            out.close();
-        } catch (IOException e) {
-            Log.e("View", "Could not dump recycler traces");
-            return;
-        }
-
-        sRecyclerViews.clear();
-        sRecyclerViews = null;
-
-        sRecyclerTraces.clear();
-        sRecyclerTraces = null;
-
-        sRecyclerOwnerView = null;
     }
 
     /**
-     * Outputs a trace to the currently opened traces file. The trace contains the class name
-     * and instance's hashcode of the specified view as well as the supplied trace type.
-     *
-     * @param view the view to trace
-     * @param type the type of the trace
+     * @deprecated This method is now unused and invoking it is a no-op
      */
+    @Deprecated
+    @SuppressWarnings({ "UnusedParameters", "deprecation" })
     public static void trace(View view, HierarchyTraceType type) {
-        if (sHierarchyTraces == null) {
-            return;
-        }
-
-        try {
-            sHierarchyTraces.write(type.name());
-            sHierarchyTraces.write(' ');
-            sHierarchyTraces.write(view.getClass().getName());
-            sHierarchyTraces.write('@');
-            sHierarchyTraces.write(Integer.toHexString(view.hashCode()));
-            sHierarchyTraces.newLine();
-        } catch (IOException e) {
-            Log.w("View", "Error while dumping trace of type " + type + " for view " + view);
-        }
     }
 
     /**
-     * Starts tracing the view hierarchy of the specified view. The trace is identified by a prefix,
-     * used to build the traces files names: <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code> and
-     * <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>.
-     *
-     * Only one view hierarchy can be traced at the same time. After calling this method, any
-     * other invocation will result in a <code>IllegalStateException</code> unless
-     * {@link #stopHierarchyTracing()} is invoked before.
-     *
-     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>
-     * containing all the traces (or method calls) relative to the specified view's hierarchy.
-     *
-     * This method will return immediately if TRACE_HIERARCHY is false.
-     *
-     * @param prefix the traces files name prefix
-     * @param view the view whose hierarchy must be traced
-     *
-     * @see #stopHierarchyTracing()
-     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
+     * @deprecated This method is now unused and invoking it is a no-op
      */
+    @Deprecated
+    @SuppressWarnings("UnusedParameters")
     public static void startHierarchyTracing(String prefix, View view) {
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (!TRACE_HIERARCHY) {
-            return;
-        }
-
-        if (sHierarchyRoot != null) {
-            throw new IllegalStateException("You must call stopHierarchyTracing() before running" +
-                " a new trace!");
-        }
-
-        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
-        //noinspection ResultOfMethodCallIgnored
-        hierarchyDump.mkdirs();
-
-        hierarchyDump = new File(hierarchyDump, prefix + ".traces");
-        sHierarchyTracePrefix = prefix;
-
-        try {
-            sHierarchyTraces = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
-        } catch (IOException e) {
-            Log.e("View", "Could not dump view hierarchy");
-            return;
-        }
-
-        sHierarchyRoot = view.getViewRootImpl();
     }
 
     /**
-     * Stops the current view hierarchy tracing. This method closes the file
-     * <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>.
-     *
-     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>
-     * containing the view hierarchy of the view supplied to
-     * {@link #startHierarchyTracing(String, View)}.
-     *
-     * This method will return immediately if TRACE_HIERARCHY is false.
-     *
-     * @see #startHierarchyTracing(String, View)
-     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
+     * @deprecated This method is now unused and invoking it is a no-op
      */
+    @Deprecated
     public static void stopHierarchyTracing() {
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (!TRACE_HIERARCHY) {
-            return;
-        }
-
-        if (sHierarchyRoot == null || sHierarchyTraces == null) {
-            throw new IllegalStateException("You must call startHierarchyTracing() before" +
-                " stopHierarchyTracing()!");
-        }
-
-        try {
-            sHierarchyTraces.close();
-        } catch (IOException e) {
-            Log.e("View", "Could not write view traces");
-        }
-        sHierarchyTraces = null;
-
-        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
-        //noinspection ResultOfMethodCallIgnored
-        hierarchyDump.mkdirs();
-        hierarchyDump = new File(hierarchyDump, sHierarchyTracePrefix + ".tree");
-
-        BufferedWriter out;
-        try {
-            out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
-        } catch (IOException e) {
-            Log.e("View", "Could not dump view hierarchy");
-            return;
-        }
-
-        View view = sHierarchyRoot.getView();
-        if (view instanceof ViewGroup) {
-            ViewGroup group = (ViewGroup) view;
-            dumpViewHierarchy(group, out, 0);
-            try {
-                out.close();
-            } catch (IOException e) {
-                Log.e("View", "Could not dump view hierarchy");
-            }
-        }
-
-        sHierarchyRoot = null;
     }
 
     static void dispatchCommand(View view, String command, String parameters,
@@ -1725,38 +1148,6 @@
         }
     }
 
-    private static void dumpViewHierarchy(ViewGroup group, BufferedWriter out, int level) {
-        if (!dumpView(group, out, level)) {
-            return;
-        }
-
-        final int count = group.getChildCount();
-        for (int i = 0; i < count; i++) {
-            final View view = group.getChildAt(i);
-            if (view instanceof ViewGroup) {
-                dumpViewHierarchy((ViewGroup) view, out, level + 1);
-            } else {
-                dumpView(view, out, level + 1);
-            }
-        }
-    }
-
-    private static boolean dumpView(Object view, BufferedWriter out, int level) {
-        try {
-            for (int i = 0; i < level; i++) {
-                out.write(' ');
-            }
-            out.write(view.getClass().getName());
-            out.write('@');
-            out.write(Integer.toHexString(view.hashCode()));
-            out.newLine();
-        } catch (IOException e) {
-            Log.w("View", "Error while dumping hierarchy tree");
-            return false;
-        }
-        return true;
-    }
-
     private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
         if (mCapturedViewFieldsForClasses == null) {
             mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
@@ -1863,7 +1254,6 @@
     }
 
     private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
-
         if (obj == null) {
             return "null";
         }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b95ca5e..421109f 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3949,10 +3949,6 @@
      * the view hierarchy.
      */
     public final void invalidateChild(View child, final Rect dirty) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
-        }
-
         ViewParent parent = this;
 
         final AttachInfo attachInfo = mAttachInfo;
@@ -4045,10 +4041,6 @@
      * does not intersect with this ViewGroup's bounds.
      */
     public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
-        }
-
         if ((mPrivateFlags & DRAWN) == DRAWN ||
                 (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
             if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
@@ -4631,61 +4623,6 @@
     }
 
     /**
-     * @hide
-     */
-    @Override
-    protected boolean dispatchConsistencyCheck(int consistency) {
-        boolean result = super.dispatchConsistencyCheck(consistency);
-
-        final int count = mChildrenCount;
-        final View[] children = mChildren;
-        for (int i = 0; i < count; i++) {
-            if (!children[i].dispatchConsistencyCheck(consistency)) result = false;
-        }
-
-        return result;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    protected boolean onConsistencyCheck(int consistency) {
-        boolean result = super.onConsistencyCheck(consistency);
-
-        final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
-        final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
-
-        if (checkLayout) {
-            final int count = mChildrenCount;
-            final View[] children = mChildren;
-            for (int i = 0; i < count; i++) {
-                if (children[i].getParent() != this) {
-                    result = false;
-                    android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                            "View " + children[i] + " has no parent/a parent that is not " + this);
-                }
-            }
-        }
-
-        if (checkDrawing) {
-            // If this group is dirty, check that the parent is dirty as well
-            if ((mPrivateFlags & DIRTY_MASK) != 0) {
-                final ViewParent parent = getParent();
-                if (parent != null && !(parent instanceof ViewRootImpl)) {
-                    if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) {
-                        result = false;
-                        android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                                "ViewGroup " + this + " is dirty but its parent is not: " + this);
-                    }
-                }
-            }
-        }
-
-        return result;
-    }
-
-    /**
      * {@inheritDoc}
      */
     @Override
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d9e3545..c9a41ad 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -55,7 +55,6 @@
 import android.os.Trace;
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
-import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
 import android.util.TypedValue;
@@ -218,8 +217,6 @@
 
     boolean mTraversalScheduled;
     int mTraversalBarrier;
-    long mLastTraversalFinishedTimeNanos;
-    long mLastDrawFinishedTimeNanos;
     boolean mWillDrawSoon;
     boolean mFitSystemWindowsRequested;
     boolean mLayoutRequested;
@@ -987,18 +984,6 @@
                 Debug.startMethodTracing("ViewAncestor");
             }
 
-            final long traversalStartTime;
-            if (ViewDebug.DEBUG_LATENCY) {
-                traversalStartTime = System.nanoTime();
-                if (mLastTraversalFinishedTimeNanos != 0) {
-                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals(); it has been "
-                            + ((traversalStartTime - mLastTraversalFinishedTimeNanos) * 0.000001f)
-                            + "ms since the last traversals finished.");
-                } else {
-                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals().");
-                }
-            }
-
             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
             try {
                 performTraversals();
@@ -1006,14 +991,6 @@
                 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
             }
 
-            if (ViewDebug.DEBUG_LATENCY) {
-                long now = System.nanoTime();
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "performTraversals() took "
-                        + ((now - traversalStartTime) * 0.000001f)
-                        + "ms.");
-                mLastTraversalFinishedTimeNanos = now;
-            }
-
             if (mProfile) {
                 Debug.stopMethodTracing();
                 mProfile = false;
@@ -1865,28 +1842,12 @@
                     host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
         }
 
-        final long startTime;
-        if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
-            startTime = SystemClock.elapsedRealtime();
-        }
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
         try {
             host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
-
-        if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
-            EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime);
-        }
-
-        if (false && ViewDebug.consistencyCheckEnabled) {
-            if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) {
-                throw new IllegalStateException("The view hierarchy is an inconsistent state,"
-                        + "please refer to the logs with the tag "
-                        + ViewDebug.CONSISTENCY_LOG_TAG + " for more infomation.");
-            }
-        }
     }
 
     public void requestTransparentRegion(View child) {
@@ -2030,18 +1991,6 @@
             return;
         }
 
-        final long drawStartTime;
-        if (ViewDebug.DEBUG_LATENCY) {
-            drawStartTime = System.nanoTime();
-            if (mLastDrawFinishedTimeNanos != 0) {
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting draw(); it has been "
-                        + ((drawStartTime - mLastDrawFinishedTimeNanos) * 0.000001f)
-                        + "ms since the last draw finished.");
-            } else {
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting draw().");
-            }
-        }
-
         final boolean fullRedrawNeeded = mFullRedrawNeeded;
         mFullRedrawNeeded = false;
 
@@ -2054,14 +2003,6 @@
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
 
-        if (ViewDebug.DEBUG_LATENCY) {
-            long now = System.nanoTime();
-            Log.d(ViewDebug.DEBUG_LATENCY_TAG, "performDraw() took "
-                    + ((now - drawStartTime) * 0.000001f)
-                    + "ms.");
-            mLastDrawFinishedTimeNanos = now;
-        }
-
         if (mReportNextDraw) {
             mReportNextDraw = false;
 
@@ -2225,19 +2166,8 @@
             int right = dirty.right;
             int bottom = dirty.bottom;
 
-            final long lockCanvasStartTime;
-            if (ViewDebug.DEBUG_LATENCY) {
-                lockCanvasStartTime = System.nanoTime();
-            }
-
             canvas = mSurface.lockCanvas(dirty);
 
-            if (ViewDebug.DEBUG_LATENCY) {
-                long now = System.nanoTime();
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- lockCanvas() took "
-                        + ((now - lockCanvasStartTime) * 0.000001f) + "ms");
-            }
-
             if (left != dirty.left || top != dirty.top || right != dirty.right ||
                     bottom != dirty.bottom) {
                 attachInfo.mIgnoreDirtyState = true;
@@ -2257,7 +2187,7 @@
             mLayoutRequested = true;    // ask wm for a new surface next time.
             return false;
         } catch (IllegalArgumentException e) {
-            Log.e(TAG, "IllegalArgumentException locking surface", e);
+            Log.e(TAG, "Could not lock surface", e);
             // Don't assume this is due to out of memory, it could be
             // something else, and if it is something else then we could
             // kill stuff (or ourself) for no reason.
@@ -2272,11 +2202,6 @@
                 //canvas.drawARGB(255, 255, 0, 0);
             }
 
-            long startTime = 0L;
-            if (ViewDebug.DEBUG_PROFILE_DRAWING) {
-                startTime = SystemClock.elapsedRealtime();
-            }
-
             // If this bitmap's format includes an alpha channel, we
             // need to clear it before drawing so that the child will
             // properly re-composite its drawing on a transparent
@@ -2309,46 +2234,23 @@
                         ? DisplayMetrics.DENSITY_DEVICE : 0);
                 attachInfo.mSetIgnoreDirtyState = false;
 
-                final long drawStartTime;
-                if (ViewDebug.DEBUG_LATENCY) {
-                    drawStartTime = System.nanoTime();
-                }
-
                 mView.draw(canvas);
 
                 drawAccessibilityFocusedDrawableIfNeeded(canvas);
-
-                if (ViewDebug.DEBUG_LATENCY) {
-                    long now = System.nanoTime();
-                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- draw() took "
-                            + ((now - drawStartTime) * 0.000001f) + "ms");
-                }
             } finally {
                 if (!attachInfo.mSetIgnoreDirtyState) {
                     // Only clear the flag if it was not set during the mView.draw() call
                     attachInfo.mIgnoreDirtyState = false;
                 }
             }
-
-            if (false && ViewDebug.consistencyCheckEnabled) {
-                mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
-            }
-
-            if (ViewDebug.DEBUG_PROFILE_DRAWING) {
-                EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
-            }
         } finally {
-            final long unlockCanvasAndPostStartTime;
-            if (ViewDebug.DEBUG_LATENCY) {
-                unlockCanvasAndPostStartTime = System.nanoTime();
-            }
-
-            surface.unlockCanvasAndPost(canvas);
-
-            if (ViewDebug.DEBUG_LATENCY) {
-                long now = System.nanoTime();
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- unlockCanvasAndPost() took "
-                        + ((now - unlockCanvasAndPostStartTime) * 0.000001f) + "ms");
+            try {
+                surface.unlockCanvasAndPost(canvas);
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "Could not unlock surface", e);
+                mLayoutRequested = true;    // ask wm for a new surface next time.
+                //noinspection ReturnInsideFinallyBlock
+                return false;
             }
 
             if (LOCAL_LOGV) {
@@ -2993,20 +2895,6 @@
                         if (hasWindowFocus) {
                             mView.sendAccessibilityEvent(
                                     AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-                            // Give accessibility focus to the view that has input
-                            // focus if such, otherwise to the first one.
-                            if (mView instanceof ViewGroup) {
-                                ViewGroup viewGroup = (ViewGroup) mView;
-                                View focused = viewGroup.findFocus();
-                                if (focused != null) {
-                                    focused.requestAccessibilityFocus();
-                                }
-                            }
-                            // There is no accessibility focus, despite our effort
-                            // above, now just give it to the first view.
-                            if (mAccessibilityFocusedHost == null) {
-                                mView.requestAccessibilityFocus();
-                            }
                         }
                     }
                 }
@@ -3209,10 +3097,6 @@
     }
 
     private void deliverInputEvent(QueuedInputEvent q) {
-        if (ViewDebug.DEBUG_LATENCY) {
-            q.mDeliverTimeNanos = System.nanoTime();
-        }
-
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
         try {
             if (q.mEvent instanceof KeyEvent) {
@@ -3660,9 +3544,6 @@
 
     private void deliverKeyEventPostIme(QueuedInputEvent q) {
         final KeyEvent event = (KeyEvent)q.mEvent;
-        if (ViewDebug.DEBUG_LATENCY) {
-            q.mDeliverPostImeTimeNanos = System.nanoTime();
-        }
 
         // If the view went away, then the event will not be handled.
         if (mView == null || !mAdded) {
@@ -4185,11 +4066,6 @@
         public InputEvent mEvent;
         public InputEventReceiver mReceiver;
         public int mFlags;
-
-        // Used for latency calculations.
-        public long mReceiveTimeNanos;
-        public long mDeliverTimeNanos;
-        public long mDeliverPostImeTimeNanos;
     }
 
     private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
@@ -4228,12 +4104,6 @@
             InputEventReceiver receiver, int flags, boolean processImmediately) {
         QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
 
-        if (ViewDebug.DEBUG_LATENCY) {
-            q.mReceiveTimeNanos = System.nanoTime();
-            q.mDeliverTimeNanos = 0;
-            q.mDeliverPostImeTimeNanos = 0;
-        }
-
         // Always enqueue the input event in order, regardless of its time stamp.
         // We do this because the application or the IME may inject key events
         // in response to touch events and we want to ensure that the injected keys
@@ -4287,42 +4157,6 @@
             throw new IllegalStateException("finished input event out of order");
         }
 
-        if (ViewDebug.DEBUG_LATENCY) {
-            final long now = System.nanoTime();
-            final long eventTime = q.mEvent.getEventTimeNano();
-            final StringBuilder msg = new StringBuilder();
-            msg.append("Spent ");
-            msg.append((now - q.mReceiveTimeNanos) * 0.000001f);
-            msg.append("ms processing ");
-            if (q.mEvent instanceof KeyEvent) {
-                final KeyEvent  keyEvent = (KeyEvent)q.mEvent;
-                msg.append("key event, action=");
-                msg.append(KeyEvent.actionToString(keyEvent.getAction()));
-            } else {
-                final MotionEvent motionEvent = (MotionEvent)q.mEvent;
-                msg.append("motion event, action=");
-                msg.append(MotionEvent.actionToString(motionEvent.getAction()));
-                msg.append(", historySize=");
-                msg.append(motionEvent.getHistorySize());
-            }
-            msg.append(", handled=");
-            msg.append(handled);
-            msg.append(", received at +");
-            msg.append((q.mReceiveTimeNanos - eventTime) * 0.000001f);
-            if (q.mDeliverTimeNanos != 0) {
-                msg.append("ms, delivered at +");
-                msg.append((q.mDeliverTimeNanos - eventTime) * 0.000001f);
-            }
-            if (q.mDeliverPostImeTimeNanos != 0) {
-                msg.append("ms, delivered post IME at +");
-                msg.append((q.mDeliverPostImeTimeNanos - eventTime) * 0.000001f);
-            }
-            msg.append("ms, finished at +");
-            msg.append((now - eventTime) * 0.000001f);
-            msg.append("ms.");
-            Log.d(ViewDebug.DEBUG_LATENCY_TAG, msg.toString());
-        }
-
         if (q.mReceiver != null) {
             q.mReceiver.finishInputEvent(q.mEvent, handled);
         } else {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 0c5d6ea..ceb9fe6 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -386,6 +386,12 @@
          */
         public InputChannel monitorInput(String name);
 
+        /**
+         * Switch the keyboard layout for the given device.
+         * Direction should be +1 or -1 to go to the next or previous keyboard layout.
+         */
+        public void switchKeyboardLayout(int deviceId, int direction);
+
         public void shutdown();
         public void rebootSafeMode();
     }
diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
index 57628d3..f9f5b03 100644
--- a/core/java/android/webkit/SelectActionModeCallback.java
+++ b/core/java/android/webkit/SelectActionModeCallback.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.app.Activity;
 import android.app.SearchManager;
 import android.content.ClipboardManager;
 import android.content.Context;
@@ -122,6 +123,9 @@
                 Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
                 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
                 i.putExtra(SearchManager.QUERY, mWebView.getSelection());
+                if (!(mWebView.getContext() instanceof Activity)) {
+                    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                }
                 mWebView.getContext().startActivity(i);
                 break;
 
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 31824f3..6cee0f3 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1352,24 +1352,23 @@
             case ACCESSIBILITY_FOCUS_FORWARD: {
                 ViewRootImpl viewRootImpl = getViewRootImpl();
                 if (viewRootImpl == null) {
-                    break;
+                    return null;
                 }
                 View currentFocus = viewRootImpl.getAccessibilityFocusedHost();
                 if (currentFocus == null) {
-                    break;
+                    return super.focusSearch(this, direction);
                 }
                 // If we have the focus try giving it to the first child.
                 if (currentFocus == this) {
-                    final int firstVisiblePosition = getFirstVisiblePosition();
-                    if (firstVisiblePosition >= 0) {
+                    if (getChildCount() > 0) {
                         return getChildAt(0);
                     }
-                    return null;
+                    return super.focusSearch(this, direction);
                 }
                 // Find the item that has accessibility focus.
                 final int currentPosition = getPositionForView(currentFocus);
                 if (currentPosition < 0 || currentPosition >= getCount()) {
-                    break;
+                    return super.focusSearch(this, direction);
                 }
                 // Try to advance focus in the current item.
                 View currentItem = getChildAt(currentPosition - getFirstVisiblePosition());
@@ -1386,25 +1385,31 @@
                 final int nextPosition = currentPosition - getFirstVisiblePosition() + 1;
                 if (nextPosition < getChildCount()) {
                     return getChildAt(nextPosition);
+                } else {
+                    return super.focusSearch(this, direction);
                 }
-            } break;
+            }
             case ACCESSIBILITY_FOCUS_BACKWARD: {
                 ViewRootImpl viewRootImpl = getViewRootImpl();
                 if (viewRootImpl == null) {
-                    break;
+                    return null;
                 }
                 View currentFocus = viewRootImpl.getAccessibilityFocusedHost();
                 if (currentFocus == null) {
-                    break;
+                    return super.focusSearch(this, direction);
                 }
                 // If we have the focus do a generic search.
                 if (currentFocus == this) {
+                    final int lastChildIndex = getChildCount() - 1;
+                    if (lastChildIndex >= 0) {
+                        return getChildAt(lastChildIndex);
+                    }
                     return super.focusSearch(this, direction);
                 }
                 // Find the item that has accessibility focus.
                 final int currentPosition = getPositionForView(currentFocus);
                 if (currentPosition < 0 || currentPosition >= getCount()) {
-                    break;
+                    return super.focusSearch(this, direction);
                 }
                 // Try to advance focus in the current item.
                 View currentItem = getChildAt(currentPosition - getFirstVisiblePosition());
@@ -1422,7 +1427,7 @@
                 if (nextPosition >= 0) {
                     return getChildAt(nextPosition);
                 } else {
-                    return this;
+                    return super.focusSearch(this, direction);
                 }
             }
         }
@@ -2216,31 +2221,17 @@
 
         View child;
         if (scrapView != null) {
-            if (ViewDebug.TRACE_RECYCLER) {
-                ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.RECYCLE_FROM_SCRAP_HEAP,
-                        position, -1);
-            }
-
             child = mAdapter.getView(position, scrapView, this);
 
             if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
                 child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
             }
 
-            if (ViewDebug.TRACE_RECYCLER) {
-                ViewDebug.trace(child, ViewDebug.RecyclerTraceType.BIND_VIEW,
-                        position, getChildCount());
-            }
-
             if (child != scrapView) {
                 mRecycler.addScrapView(scrapView, position);
                 if (mCacheColorHint != 0) {
                     child.setDrawingCacheBackgroundColor(mCacheColorHint);
                 }
-                if (ViewDebug.TRACE_RECYCLER) {
-                    ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
-                            position, -1);
-                }
             } else {
                 isScrap[0] = true;
                 child.dispatchFinishTemporaryDetach();
@@ -2255,10 +2246,6 @@
             if (mCacheColorHint != 0) {
                 child.setDrawingCacheBackgroundColor(mCacheColorHint);
             }
-            if (ViewDebug.TRACE_RECYCLER) {
-                ViewDebug.trace(child, ViewDebug.RecyclerTraceType.NEW_VIEW,
-                        position, getChildCount());
-            }
         }
 
         if (mAdapterHasStableIds) {
@@ -4959,12 +4946,6 @@
                     int position = firstPosition + i;
                     if (position >= headerViewsCount && position < footerViewsStart) {
                         mRecycler.addScrapView(child, position);
-
-                        if (ViewDebug.TRACE_RECYCLER) {
-                            ViewDebug.trace(child,
-                                    ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
-                                    firstPosition + i, -1);
-                        }
                     }
                 }
             }
@@ -4983,12 +4964,6 @@
                     int position = firstPosition + i;
                     if (position >= headerViewsCount && position < footerViewsStart) {
                         mRecycler.addScrapView(child, position);
-
-                        if (ViewDebug.TRACE_RECYCLER) {
-                            ViewDebug.trace(child,
-                                    ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
-                                    firstPosition + i, -1);
-                        }
                     }
                 }
             }
@@ -5932,63 +5907,6 @@
         removeAllViewsInLayout();
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    protected boolean onConsistencyCheck(int consistency) {
-        boolean result = super.onConsistencyCheck(consistency);
-
-        final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
-
-        if (checkLayout) {
-            // The active recycler must be empty
-            final View[] activeViews = mRecycler.mActiveViews;
-            int count = activeViews.length;
-            for (int i = 0; i < count; i++) {
-                if (activeViews[i] != null) {
-                    result = false;
-                    Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                            "AbsListView " + this + " has a view in its active recycler: " +
-                                    activeViews[i]);
-                }
-            }
-
-            // All views in the recycler must NOT be on screen and must NOT have a parent
-            final ArrayList<View> scrap = mRecycler.mCurrentScrap;
-            if (!checkScrap(scrap)) result = false;
-            final ArrayList<View>[] scraps = mRecycler.mScrapViews;
-            count = scraps.length;
-            for (int i = 0; i < count; i++) {
-                if (!checkScrap(scraps[i])) result = false;
-            }
-        }
-
-        return result;
-    }
-
-    private boolean checkScrap(ArrayList<View> scrap) {
-        if (scrap == null) return true;
-        boolean result = true;
-
-        final int count = scrap.size();
-        for (int i = 0; i < count; i++) {
-            final View view = scrap.get(i);
-            if (view.getParent() != null) {
-                result = false;
-                Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
-                        " has a view in its scrap heap still attached to a parent: " + view);
-            }
-            if (indexOfChild(view) >= 0) {
-                result = false;
-                Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
-                        " has a view in its scrap heap that is also a direct child: " + view);
-            }
-        }
-
-        return result;
-    }
-
     private void finishGlows() {
         if (mEdgeGlowTop != null) {
             mEdgeGlowTop.finish();
@@ -6544,12 +6462,6 @@
                     if (hasListener) {
                         mRecyclerListener.onMovedToScrapHeap(victim);
                     }
-
-                    if (ViewDebug.TRACE_RECYCLER) {
-                        ViewDebug.trace(victim,
-                                ViewDebug.RecyclerTraceType.MOVE_FROM_ACTIVE_TO_SCRAP_HEAP,
-                                mFirstActivePosition + i, -1);
-                    }
                 }
             }
 
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index 6658fc2..e4e7239 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -56,7 +56,12 @@
  * @attr ref android.R.styleable#Gallery_animationDuration
  * @attr ref android.R.styleable#Gallery_spacing
  * @attr ref android.R.styleable#Gallery_gravity
+ * 
+ * @deprecated This widget is no longer supported. Other horizontally scrolling
+ * widgets include {@link HorizontalScrollView} and {@link android.support.v4.view.ViewPager}
+ * from the support library.
  */
+@Deprecated
 @Widget
 public class Gallery extends AbsSpinner implements GestureDetector.OnGestureListener {
 
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 60a1d15..772d748 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -294,8 +294,32 @@
     }
 
     /**
-     * Orientation is used only to generate default row/column indices when
-     * they are not specified by a component's layout parameters.
+     *
+     * GridLayout uses the orientation property for two purposes:
+     * <ul>
+     *  <li>
+     *      To control the 'direction' in which default row/column indices are generated
+     *      when they are not specified in a component's layout parameters.
+     *  </li>
+     *  <li>
+     *      To control which axis should be processed first during the layout operation:
+     *      when orientation is {@link #HORIZONTAL} the horizontal axis is laid out first.
+     *  </li>
+     * </ul>
+     *
+     * The order in which axes are laid out is important if, for example, the height of
+     * one of GridLayout's children is dependent on its width - and its width is, in turn,
+     * dependent on the widths of other components.
+     * <p>
+     * If your layout contains a {@link TextView} (or derivative:
+     * {@code Button}, {@code EditText}, {@code CheckBox}, etc.) which is
+     * in multi-line mode (the default) it is normally best to leave GridLayout's
+     * orientation as {@code HORIZONTAL} - because {@code TextView} is capable of
+     * deriving its height for a given width, but not the other way around.
+     * <p>
+     * Other than the effects above, orientation does not affect the actual layout operation of
+     * GridLayout, so it's fine to leave GridLayout in {@code HORIZONTAL} mode even if
+     * the height of the intended layout greatly exceeds its width.
      * <p>
      * The default value of this property is {@link #HORIZONTAL}.
      *
@@ -1373,6 +1397,7 @@
                             break;
                         }
                         case PENDING: {
+                            // le singe est dans l'arbre
                             assert false;
                             break;
                         }
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 5098523..c62b62b 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1557,10 +1557,6 @@
             if (dataChanged) {
                 for (int i = 0; i < childCount; i++) {
                     recycleBin.addScrapView(getChildAt(i), firstPosition+i);
-                    if (ViewDebug.TRACE_RECYCLER) {
-                        ViewDebug.trace(getChildAt(i),
-                                ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, index, i);
-                    }
                 }
             } else {
                 recycleBin.fillActiveViews(childCount, firstPosition);
@@ -1757,11 +1753,6 @@
             // Try to use an existing view for this position
             child = mRecycler.getActiveView(position);
             if (child != null) {
-                if (ViewDebug.TRACE_RECYCLER) {
-                    ViewDebug.trace(child, ViewDebug.RecyclerTraceType.RECYCLE_FROM_ACTIVE_HEAP,
-                            position, getChildCount());
-                }
-
                 // Found it -- we're using an existing child
                 // This just needs to be positioned
                 setupChild(child, position, y, flow, childrenLeft, selected, true);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 51c957a..1985792 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -37,6 +37,7 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater.Filter;
 import android.view.RemotableViewMethod;
@@ -1182,6 +1183,87 @@
     }
 
     /**
+     * Helper action to set text size on a TextView in any supported units.
+     */
+    private class TextViewSizeAction extends Action {
+        public TextViewSizeAction(int viewId, int units, float size) {
+            this.viewId = viewId;
+            this.units = units;
+            this.size = size;
+        }
+
+        public TextViewSizeAction(Parcel parcel) {
+            viewId = parcel.readInt();
+            units = parcel.readInt();
+            size  = parcel.readFloat();
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(TAG);
+            dest.writeInt(viewId);
+            dest.writeInt(units);
+            dest.writeFloat(size);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent) {
+            final Context context = root.getContext();
+            final TextView target = (TextView) root.findViewById(viewId);
+            if (target == null) return;
+            target.setTextSize(units, size);
+        }
+
+        int viewId;
+        int units;
+        float size;
+
+        public final static int TAG = 13;
+    }
+
+    /**
+     * Helper action to set padding on a View.
+     */
+    private class ViewPaddingAction extends Action {
+        public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
+            this.viewId = viewId;
+            this.left = left;
+            this.top = top;
+            this.right = right;
+            this.bottom = bottom;
+        }
+
+        public ViewPaddingAction(Parcel parcel) {
+            viewId = parcel.readInt();
+            left = parcel.readInt();
+            top = parcel.readInt();
+            right = parcel.readInt();
+            bottom = parcel.readInt();
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(TAG);
+            dest.writeInt(viewId);
+            dest.writeInt(left);
+            dest.writeInt(top);
+            dest.writeInt(right);
+            dest.writeInt(bottom);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent) {
+            final Context context = root.getContext();
+            final View target = root.findViewById(viewId);
+            if (target == null) return;
+            target.setPadding(left, top, right, bottom);
+        }
+
+        int viewId;
+        int left, top, right, bottom;
+
+        public final static int TAG = 14;
+    }
+
+    /**
      * Simple class used to keep track of memory usage in a RemoteViews.
      *
      */
@@ -1334,6 +1416,12 @@
                     case TextViewDrawableAction.TAG:
                         mActions.add(new TextViewDrawableAction(parcel));
                         break;
+                    case TextViewSizeAction.TAG:
+                        mActions.add(new TextViewSizeAction(parcel));
+                        break;
+                    case ViewPaddingAction.TAG:
+                        mActions.add(new ViewPaddingAction(parcel));
+                        break;
                     case BitmapReflectionAction.TAG:
                         mActions.add(new BitmapReflectionAction(parcel));
                         break;
@@ -1541,7 +1629,19 @@
     public void setTextViewText(int viewId, CharSequence text) {
         setCharSequence(viewId, "setText", text);
     }
-    
+
+    /**
+     * @hide
+     * Equivalent to calling {@link TextView#setTextSize(int, float)}
+     * 
+     * @param viewId The id of the view whose text size should change
+     * @param units The units of size (e.g. COMPLEX_UNIT_SP)
+     * @param size The size of the text
+     */
+    public void setTextViewTextSize(int viewId, int units, float size) {
+        addAction(new TextViewSizeAction(viewId, units, size));
+    }
+
     /**
      * Equivalent to calling 
      * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
@@ -1799,6 +1899,20 @@
     }
 
     /**
+     * @hide
+     * Equivalent to calling {@link View#setPadding(int, int, int, int)}.
+     *
+     * @param viewId The id of the view to change
+     * @param left the left padding in pixels
+     * @param top the top padding in pixels
+     * @param right the right padding in pixels
+     * @param bottom the bottom padding in pixels
+     */
+    public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
+        addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
+    }
+
+    /**
      * Call a method taking one boolean on a view in the layout for this RemoteViews.
      *
      * @param viewId The id of the view on which to call the method.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 112881c..81a44fd 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -39,6 +39,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.text.BoringLayout;
 import android.text.DynamicLayout;
 import android.text.Editable;
@@ -5625,6 +5626,7 @@
                       physicalWidth, false);
     }
 
+    /** @hide */
     @Override
     public void onResolvedLayoutDirectionReset() {
         if (mLayoutAlignment != null) {
@@ -7704,14 +7706,23 @@
         super.onPopulateAccessibilityEvent(event);
 
         final boolean isPassword = hasPasswordTransformationMethod();
-        if (!isPassword) {
-            CharSequence text = getTextForAccessibility();
+        if (!isPassword || shouldSpeakPasswordsForAccessibility()) {
+            final CharSequence text = getTextForAccessibility();
             if (!TextUtils.isEmpty(text)) {
                 event.getText().add(text);
             }
         }
     }
 
+    /**
+     * @return true if the user has explicitly allowed accessibility services
+     * to speak passwords.
+     */
+    private boolean shouldSpeakPasswordsForAccessibility() {
+        return (Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) == 1);
+    }
+
     @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
@@ -8161,6 +8172,7 @@
         return mEditor.mInBatchEditControllers;
     }
 
+    /** @hide */
     @Override
     public void onResolvedTextDirectionChanged() {
         if (hasPasswordTransformationMethod()) {
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 88d7e05..fafc113 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -120,7 +120,12 @@
      */
     public void cancel() {
         mTN.hide();
-        // TODO this still needs to cancel the inflight notification if any
+
+        try {
+            getService().cancelToast(mContext.getPackageName(), mTN);
+        } catch (RemoteException e) {
+            // Empty
+        }
     }
     
     /**
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 614f73f..7334ac3 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -20,6 +20,7 @@
 import com.android.internal.content.PackageMonitor;
 
 import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -34,6 +35,9 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.PatternMatcher;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserId;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -61,6 +65,7 @@
 public class ResolverActivity extends AlertActivity implements AdapterView.OnItemClickListener {
     private static final String TAG = "ResolverActivity";
 
+    private int mLaunchedFromUid;
     private ResolveListAdapter mAdapter;
     private PackageManager mPm;
     private boolean mAlwaysUseOption;
@@ -102,6 +107,12 @@
             boolean alwaysUseOption) {
         setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert);
         super.onCreate(savedInstanceState);
+        try {
+            mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
+                    getActivityToken());
+        } catch (RemoteException e) {
+            mLaunchedFromUid = -1;
+        }
         mPm = getPackageManager();
         mAlwaysUseOption = alwaysUseOption;
         mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
@@ -118,9 +129,14 @@
         mIconDpi = am.getLauncherLargeIconDensity();
         mIconSize = am.getLauncherLargeIconSize();
 
-        mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList);
+        mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,
+                mLaunchedFromUid);
         int count = mAdapter.getCount();
-        if (count > 1) {
+        if (mLaunchedFromUid < 0 || UserId.isIsolated(mLaunchedFromUid)) {
+            // Gulp!
+            finish();
+            return;
+        } else if (count > 1) {
             ap.mView = getLayoutInflater().inflate(R.layout.resolver_grid, null);
             mGrid = (GridView) ap.mView.findViewById(R.id.resolver_grid);
             mGrid.setAdapter(mAdapter);
@@ -146,9 +162,13 @@
 
         if (alwaysUseOption) {
             final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
-            buttonLayout.setVisibility(View.VISIBLE);
-            mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
-            mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
+            if (buttonLayout != null) {
+                buttonLayout.setVisibility(View.VISIBLE);
+                mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
+                mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
+            } else {
+                mAlwaysUseOption = false;
+            }
         }
     }
 
@@ -207,6 +227,18 @@
             mPackageMonitor.unregister();
             mRegistered = false;
         }
+        if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+            // This resolver is in the unusual situation where it has been
+            // launched at the top of a new task.  We don't let it be added
+            // to the recent tasks shown to the user, and we need to make sure
+            // that each time we are launched we get the correct launching
+            // uid (not re-using the same resolver from an old launching uid),
+            // so we will now finish ourself since being no longer visible,
+            // the user probably can't get back to us.
+            if (!isChangingConfigurations()) {
+                finish();
+            }
+        }
     }
 
     @Override
@@ -363,17 +395,19 @@
         private final Intent[] mInitialIntents;
         private final List<ResolveInfo> mBaseResolveList;
         private final Intent mIntent;
+        private final int mLaunchedFromUid;
         private final LayoutInflater mInflater;
 
         private List<ResolveInfo> mCurrentResolveList;
         private List<DisplayResolveInfo> mList;
 
         public ResolveListAdapter(Context context, Intent intent,
-                Intent[] initialIntents, List<ResolveInfo> rList) {
+                Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid) {
             mIntent = new Intent(intent);
             mIntent.setComponent(null);
             mInitialIntents = initialIntents;
             mBaseResolveList = rList;
+            mLaunchedFromUid = launchedFromUid;
             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
             rebuildList();
         }
@@ -400,6 +434,23 @@
                 mCurrentResolveList = mPm.queryIntentActivities(
                         mIntent, PackageManager.MATCH_DEFAULT_ONLY
                         | (mAlwaysUseOption ? PackageManager.GET_RESOLVED_FILTER : 0));
+                // Filter out any activities that the launched uid does not
+                // have permission for.  We don't do this when we have an explicit
+                // list of resolved activities, because that only happens when
+                // we are being subclassed, so we can safely launch whatever
+                // they gave us.
+                if (mCurrentResolveList != null) {
+                    for (int i=mCurrentResolveList.size()-1; i >= 0; i--) {
+                        ActivityInfo ai = mCurrentResolveList.get(i).activityInfo;
+                        int granted = ActivityManager.checkComponentPermission(
+                                ai.permission, mLaunchedFromUid,
+                                ai.applicationInfo.uid, ai.exported);
+                        if (granted != PackageManager.PERMISSION_GRANTED) {
+                            // Access not allowed!
+                            mCurrentResolveList.remove(i);
+                        }
+                    }
+                }
             }
             int N;
             if ((mCurrentResolveList != null) && ((N = mCurrentResolveList.size()) > 0)) {
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index 6136206..d6fb847 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -67,6 +67,7 @@
         public void onReleased(View v, int handle);
         public void onTrigger(View v, int target);
         public void onGrabbedStateChange(View v, int handle);
+        public void onFinishFinalAnimation();
     }
 
     // Tuneable parameters for animation
@@ -77,9 +78,13 @@
     private static final int HIDE_ANIMATION_DELAY = 200;
     private static final int HIDE_ANIMATION_DURATION = 200;
     private static final int SHOW_ANIMATION_DURATION = 200;
-    private static final int SHOW_ANIMATION_DELAY = 0;
+    private static final int SHOW_ANIMATION_DELAY = 50;
     private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
-    private static final float TARGET_INITIAL_POSITION_SCALE = 0.8f;
+    private static final float TARGET_SCALE_SELECTED = 0.8f;
+    private static final long INITIAL_SHOW_HANDLE_DURATION = 200;
+    private static final float TARGET_SCALE_UNSELECTED = 1.0f;
+    private static final float RING_SCALE_UNSELECTED = 0.5f;
+    private static final float RING_SCALE_SELECTED = 1.5f;
 
     private TimeInterpolator mChevronAnimationInterpolator = Ease.Quad.easeOut;
 
@@ -126,6 +131,15 @@
             }
         }
 
+        public void cancel() {
+            final int count = size();
+            for (int i = 0; i < count; i++) {
+                Tweener anim = get(i);
+                anim.animator.cancel();
+            }
+            clear();
+        }
+
         public void stop() {
             final int count = size();
             for (int i = 0; i < count; i++) {
@@ -143,6 +157,7 @@
     private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
         public void onAnimationEnd(Animator animator) {
             switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
+            dispatchOnFinishFinalAnimation();
         }
     };
 
@@ -150,6 +165,7 @@
         public void onAnimationEnd(Animator animator) {
             ping();
             switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
+            dispatchOnFinishFinalAnimation();
         }
     };
 
@@ -349,7 +365,7 @@
                 stopHandleAnimation();
                 deactivateTargets();
                 showTargets(true);
-                mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE);
+                activateHandle();
                 setGrabbedState(OnTriggerListener.CENTER_HANDLE);
                 if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                     announceTargets();
@@ -368,6 +384,19 @@
         }
     }
 
+    private void activateHandle() {
+        mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE);
+        if (mAlwaysTrackFinger) {
+            mHandleAnimations.stop();
+            mHandleDrawable.setAlpha(0.0f);
+            mHandleAnimations.add(Tweener.to(mHandleDrawable, INITIAL_SHOW_HANDLE_DURATION,
+                    "ease", Ease.Cubic.easeIn,
+                    "alpha", 1.0f,
+                    "onUpdate", mUpdateListener));
+            mHandleAnimations.start();
+        }
+    }
+
     /**
      * Animation used to attract user's attention to the target button.
      * Assumes mChevronDrawables is an a list with an even number of chevrons filled with
@@ -463,6 +492,12 @@
         }
     }
 
+    private void dispatchOnFinishFinalAnimation() {
+        if (mOnTriggerListener != null) {
+            mOnTriggerListener.onFinishFinalAnimation();
+        }
+    }
+
     private void doFinish() {
         final int activeTarget = mActiveTarget;
         boolean targetHit =  activeTarget != -1;
@@ -471,8 +506,9 @@
         hideTargets(true);
 
         // Highlight the selected one
-        mHandleDrawable.setAlpha(targetHit ? 0.0f : 1.0f);
+        mHandleAnimations.cancel();
         if (targetHit) {
+            mHandleDrawable.setAlpha(0.0f);
             mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
             hideUnselected(activeTarget);
 
@@ -483,12 +519,11 @@
 
         // Animate handle back to the center based on current state.
         int delay = targetHit ? RETURN_TO_HOME_DELAY : 0;
-        int duration = targetHit ? 0 : RETURN_TO_HOME_DURATION;
-        mHandleAnimations.stop();
+        int duration = RETURN_TO_HOME_DURATION;
         mHandleAnimations.add(Tweener.to(mHandleDrawable, duration,
                 "ease", Ease.Quart.easeOut,
                 "delay", delay,
-                "alpha", 1.0f,
+                "alpha", mAlwaysTrackFinger ? 0.0f : 1.0f,
                 "x", 0,
                 "y", 0,
                 "onUpdate", mUpdateListener,
@@ -508,12 +543,15 @@
     }
 
     private void hideTargets(boolean animate) {
-        mTargetAnimations.stop();
+        mTargetAnimations.cancel();
         // Note: these animations should complete at the same time so that we can swap out
         // the target assets asynchronously from the setTargetResources() call.
         mAnimatingTargets = animate;
         final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
         final int delay = animate ? HIDE_ANIMATION_DELAY : 0;
+        final boolean targetSelected = mActiveTarget != -1;
+
+        final float targetScale = targetSelected ? TARGET_SCALE_SELECTED : TARGET_SCALE_UNSELECTED;
         final int length = mTargetDrawables.size();
         for (int i = 0; i < length; i++) {
             TargetDrawable target = mTargetDrawables.get(i);
@@ -521,13 +559,13 @@
             mTargetAnimations.add(Tweener.to(target, duration,
                     "ease", Ease.Cubic.easeOut,
                     "alpha", 0.0f,
-                    "scaleX", TARGET_INITIAL_POSITION_SCALE,
-                    "scaleY", TARGET_INITIAL_POSITION_SCALE,
+                    "scaleX", targetScale,
+                    "scaleY", targetScale,
                     "delay", delay,
                     "onUpdate", mUpdateListener));
         }
 
-        float ringScaleTarget = mActiveTarget != -1 ? 1.5f : 0.5f;
+        final float ringScaleTarget = targetSelected ? RING_SCALE_SELECTED : RING_SCALE_UNSELECTED;
         mTargetAnimations.add(Tweener.to(mOuterRing, duration,
                 "ease", Ease.Cubic.easeOut,
                 "alpha", 0.0f,
@@ -544,13 +582,14 @@
         mTargetAnimations.stop();
         mAnimatingTargets = animate;
         final int delay = animate ? SHOW_ANIMATION_DELAY : 0;
+        final int duration = animate ? SHOW_ANIMATION_DURATION : 0;
         final int length = mTargetDrawables.size();
         for (int i = 0; i < length; i++) {
             TargetDrawable target = mTargetDrawables.get(i);
             target.setState(TargetDrawable.STATE_INACTIVE);
-            target.setScaleX(TARGET_INITIAL_POSITION_SCALE);
-            target.setScaleY(TARGET_INITIAL_POSITION_SCALE);
-            mTargetAnimations.add(Tweener.to(target, animate ? SHOW_ANIMATION_DURATION : 0,
+            target.setScaleX(TARGET_SCALE_SELECTED);
+            target.setScaleY(TARGET_SCALE_SELECTED);
+            mTargetAnimations.add(Tweener.to(target, duration,
                     "ease", Ease.Cubic.easeOut,
                     "alpha", 1.0f,
                     "scaleX", 1.0f,
@@ -558,9 +597,7 @@
                     "delay", delay,
                     "onUpdate", mUpdateListener));
         }
-        mOuterRing.setScaleX(0.5f);
-        mOuterRing.setScaleY(0.5f);
-        mTargetAnimations.add(Tweener.to(mOuterRing, animate ? SHOW_ANIMATION_DURATION : 0,
+        mTargetAnimations.add(Tweener.to(mOuterRing, duration,
                 "ease", Ease.Cubic.easeOut,
                 "alpha", 1.0f,
                 "scaleX", 1.0f,
@@ -572,10 +609,6 @@
         mTargetAnimations.start();
     }
 
-    private void stopTargetAnimation() {
-        mTargetAnimations.stop();
-    }
-
     private void vibrate() {
         if (mVibrator != null) {
             mVibrator.vibrate(mVibrationDuration);
@@ -708,7 +741,7 @@
     public void reset(boolean animate) {
         stopChevronAnimation();
         stopHandleAnimation();
-        stopTargetAnimation();
+        mTargetAnimations.stop();
         hideChevrons();
         hideTargets(animate);
         mHandleDrawable.setX(0);
@@ -760,7 +793,7 @@
     private void handleDown(MotionEvent event) {
        if (!trySwitchToFirstTouchState(event.getX(), event.getY())) {
             mDragging = false;
-            stopTargetAnimation();
+            mTargetAnimations.cancel();
             ping();
         }
     }
@@ -815,8 +848,8 @@
                     // For more than one target, snap to the closest one less than hitRadius away.
                     float best = Float.MAX_VALUE;
                     final float hitRadius2 = mHitRadius * mHitRadius;
+                    // Find first target in range
                     for (int i = 0; i < ntargets; i++) {
-                        // Snap to the first target in range
                         TargetDrawable target = targets.get(i);
                         float dx = limitX - target.getX();
                         float dy = limitY - target.getY();
@@ -842,10 +875,15 @@
             float newX = singleTarget ? x : target.getX();
             float newY = singleTarget ? y : target.getY();
             moveHandleTo(newX, newY, false);
+            mHandleAnimations.cancel();
+            mHandleDrawable.setAlpha(0.0f);
         } else {
             switchToState(STATE_TRACKING, x, y);
+            if (mActiveTarget != -1) {
+                mHandleAnimations.cancel();
+                mHandleDrawable.setAlpha(1.0f);
+            }
             moveHandleTo(x, y, false);
-            mHandleDrawable.setAlpha(1.0f);
         }
 
         // Draw handle outside parent's bounds
@@ -857,7 +895,6 @@
                 TargetDrawable target = targets.get(mActiveTarget);
                 if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
                     target.setState(TargetDrawable.STATE_INACTIVE);
-                    mHandleDrawable.setAlpha(1.0f);
                 }
             }
             // Focus the new target
@@ -865,7 +902,6 @@
                 TargetDrawable target = targets.get(activeTarget);
                 if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
                     target.setState(TargetDrawable.STATE_FOCUSED);
-                    mHandleDrawable.setAlpha(0.0f);
                 }
                 dispatchGrabbedEvent(activeTarget);
                 if (AccessibilityManager.getInstance(mContext).isEnabled()) {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 7146667..d422951 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -703,7 +703,7 @@
 #endif
     uint32_t ref = ident;
     if (resolve) {
-        block = res.resolveReference(&value, block, &ref);
+        block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config);
 #if THROW_ON_BAD_ID
         if (block == BAD_INDEX) {
             jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index e3d11f2..82f8984 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -774,11 +774,12 @@
     float transform[16];
     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface));
 
-    surfaceTexture->updateTexImage();
-    surfaceTexture->getTransformMatrix(transform);
-    GLenum renderTarget = surfaceTexture->getCurrentTextureTarget();
+    if (surfaceTexture->updateTexImage() == NO_ERROR) {
+        surfaceTexture->getTransformMatrix(transform);
+        GLenum renderTarget = surfaceTexture->getCurrentTextureTarget();
 
-    LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, renderTarget, transform);
+        LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, renderTarget, transform);
+    }
 }
 
 static void android_view_GLES20Canvas_updateRenderLayer(JNIEnv* env, jobject clazz,
diff --git a/core/res/res/drawable-hdpi/ic_settings_language.png b/core/res/res/drawable-hdpi/ic_settings_language.png
new file mode 100755
index 0000000..f635b2e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_settings_language.png
Binary files differ
diff --git a/core/res/res/drawable-large-nodpi/default_wallpaper.jpg b/core/res/res/drawable-large-nodpi/default_wallpaper.jpg
deleted file mode 100644
index 355286e..0000000
--- a/core/res/res/drawable-large-nodpi/default_wallpaper.jpg
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_settings_language.png b/core/res/res/drawable-mdpi/ic_settings_language.png
new file mode 100644
index 0000000..f8aca67
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_settings_language.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/default_wallpaper.jpg b/core/res/res/drawable-nodpi/default_wallpaper.jpg
index 7e92243..d7475b4c 100644
--- a/core/res/res/drawable-nodpi/default_wallpaper.jpg
+++ b/core/res/res/drawable-nodpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.jpg b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.jpg
new file mode 100644
index 0000000..03a14c0
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.jpg b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.jpg
new file mode 100644
index 0000000..543d118
--- /dev/null
+++ b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/default_wallpaper.jpg b/core/res/res/drawable-xhdpi/default_wallpaper.jpg
new file mode 100644
index 0000000..5b8d1d5
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_settings_language.png b/core/res/res/drawable-xhdpi/ic_settings_language.png
new file mode 100644
index 0000000..2c42db3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_settings_language.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-nodpi/default_wallpaper.jpg b/core/res/res/drawable-xlarge-nodpi/default_wallpaper.jpg
deleted file mode 100644
index 355286e..0000000
--- a/core/res/res/drawable-xlarge-nodpi/default_wallpaper.jpg
+++ /dev/null
Binary files differ
diff --git a/core/res/res/layout/notification_template_inbox.xml b/core/res/res/layout/notification_template_inbox.xml
index eb5e759..3b00feb 100644
--- a/core/res/res/layout/notification_template_inbox.xml
+++ b/core/res/res/layout/notification_template_inbox.xml
@@ -132,6 +132,34 @@
             android:visibility="gone"
             android:layout_weight="1"
             />
+        <TextView android:id="@+id/inbox_text5"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            />
+        <TextView android:id="@+id/inbox_text6"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            />
+        <TextView android:id="@+id/inbox_more"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            android:text="@android:string/ellipsis"
+            />
         <include
             layout="@layout/notification_action_list"
             android:id="@+id/actions"
diff --git a/core/res/res/raw/accessibility_gestures.bin b/core/res/res/raw/accessibility_gestures.bin
index 96fa1ec..acd7993 100644
--- a/core/res/res/raw/accessibility_gestures.bin
+++ b/core/res/res/raw/accessibility_gestures.bin
Binary files differ
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a24e345c..bbb2e8e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -208,6 +208,9 @@
   <java-symbol type="id" name="inbox_text2" />
   <java-symbol type="id" name="inbox_text3" />
   <java-symbol type="id" name="inbox_text4" />
+  <java-symbol type="id" name="inbox_text5" />
+  <java-symbol type="id" name="inbox_text6" />
+  <java-symbol type="id" name="inbox_more" />
   <java-symbol type="id" name="status_bar_latest_event_content" />
 
   <java-symbol type="attr" name="actionModeShareDrawable" />
@@ -1489,6 +1492,8 @@
   <java-symbol type="string" name="low_internal_storage_view_title" />
   <java-symbol type="string" name="report" />
   <java-symbol type="string" name="select_input_method" />
+  <java-symbol type="string" name="select_keyboard_layout_notification_title" />
+  <java-symbol type="string" name="select_keyboard_layout_notification_message" />
   <java-symbol type="string" name="smv_application" />
   <java-symbol type="string" name="smv_process" />
   <java-symbol type="string" name="tethered_notification_message" />
@@ -1513,6 +1518,8 @@
   <java-symbol type="xml" name="storage_list" />
   <java-symbol type="bool" name="config_enableDreams" />
   <java-symbol type="string" name="config_defaultDreamComponent" />
+  <java-symbol type="string" name="enable_explore_by_touch_warning_title" />
+  <java-symbol type="string" name="enable_explore_by_touch_warning_message" />
 
   <java-symbol type="layout" name="resolver_grid" />
   <java-symbol type="id" name="resolver_grid" />
@@ -1580,6 +1587,7 @@
   <java-symbol type="drawable" name="expander_ic_minimized" />
   <java-symbol type="drawable" name="ic_menu_archive" />
   <java-symbol type="drawable" name="ic_menu_goto" />
+  <java-symbol type="drawable" name="ic_settings_language" />
   <java-symbol type="drawable" name="title_bar_medium" />
   <java-symbol type="id" name="body" />
   <java-symbol type="string" name="fast_scroll_alphabet" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4511201..5929439 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2503,6 +2503,25 @@
     <!-- SearchView accessibility description for voice button [CHAR LIMIT=NONE] -->
     <string name="searchview_description_voice">Voice search</string>
 
+    <!-- Title for a warning message about the interaction model changes after allowing an accessibility
+         service to put the device into explore by touch mode, displayed as a dialog message when
+         the user selects to enables the service. (default). [CHAR LIMIT=35] -->
+    <string name="enable_explore_by_touch_warning_title">Enable Explore by Touch?</string>
+    <!-- Summary for a warning message about the interaction model changes after allowing an accessibility
+         service to put the device into explore by touch mode, displayed as a dialog message when
+         the user selects to enables the service. (tablet). [CHAR LIMIT=NONE] -->
+    <string name="enable_explore_by_touch_warning_message" product="tablet">
+            <xliff:g id="accessibility_service_name">%1$s</xliff:g> wants to enable Explore by Touch.
+            When Explore by Touch is turned on, you can hear or see descriptions of what\'s under
+            your finger or perform gestures to interact with the tablet.</string>
+    <!-- Summary for a warning message about the interaction model changes after allowing an accessibility
+         service to put the device into explore by touch mode, displayed as a dialog message when
+         the user selects to enables the service. (default). [CHAR LIMIT=NONE] -->
+    <string name="enable_explore_by_touch_warning_message" product="default">
+            <xliff:g id="accessibility_service_name">%1$s</xliff:g> wants to enable Explore by Touch.
+            When Explore by Touch is turned on, you can hear or see descriptions of what\'s under
+            your finger or perform gestures to interact with the phone.</string>
+
     <!-- String used to display the date. This is the string to say something happened 1 month ago. -->
     <string name="oneMonthDurationPast">1 month ago</string>
     <!-- String used to display the date. This is the string to say something happened more than 1 month ago. -->
@@ -3092,6 +3111,11 @@
     <!-- Title of the physical keyboard category in the input method selector [CHAR LIMIT=10] -->
     <string name="hardware">Hardware</string>
 
+    <!-- Title of the notification to prompt the user to select a keyboard layout. -->
+    <string name="select_keyboard_layout_notification_title">Select keyboard layout</string>
+    <!-- Message of the notification to prompt the user to select a keyboard layout. -->
+    <string name="select_keyboard_layout_notification_message">Touch to select a keyboard layout.</string>
+
     <string name="fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
     <string name="fast_scroll_numeric_alphabet">\u00200123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
 
@@ -3572,6 +3596,6 @@
 
     <!-- Title for a button to choose the currently selected activity
          from the activity resolver to use just this once. [CHAR LIMIT=25] -->
-    <string name="activity_resolver_use_once">Just Once</string>
+    <string name="activity_resolver_use_once">Just once</string>
 
 </resources>
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 544076f..782a701 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -252,6 +252,7 @@
     label:                              ' '
     base:                               ' '
     alt, meta:                          fallback SEARCH
+    ctrl:                               fallback LANGUAGE_SWITCH
 }
 
 key ENTER {
diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm
index e592013..d90b790 100644
--- a/data/keyboards/Virtual.kcm
+++ b/data/keyboards/Virtual.kcm
@@ -249,6 +249,7 @@
     label:                              ' '
     base:                               ' '
     alt, meta:                          fallback SEARCH
+    ctrl:                               fallback LANGUAGE_SWITCH
 }
 
 key ENTER {
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 5f74c01..785582c 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -386,6 +386,7 @@
 
     /**
      * Get the resolved layout direction of this Drawable.
+     * @hide
      */
     public int getResolvedLayoutDirectionSelf() {
         final Callback callback = getCallback();
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index aa29444..da01c44 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -59,6 +59,7 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.provider.Settings.System;
+import android.speech.RecognizerIntent;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -3818,24 +3819,32 @@
      * Tell the system to start voice-based interactions / voice commands
      */
     private void startVoiceBasedInteractions(boolean needWakeLock) {
-        Intent voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+        Intent voiceIntent = null;
+        // select which type of search to launch:
+        // - screen on and device unlocked: action is ACTION_WEB_SEARCH
+        // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
+        //    with EXTRA_SECURE set to true if the device is securely locked
+        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
+        if (!isLocked && pm.isScreenOn()) {
+            voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+        } else {
+            voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+            voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
+                    isLocked && mKeyguardManager.isKeyguardSecure());
+        }
+        // start the search activity
         if (needWakeLock) {
             mMediaEventWakeLock.acquire();
         }
-        voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
         try {
-            if (mKeyguardManager != null) {
-                // it's ok to start voice-based interactions when:
-                // - the device is locked but doesn't require a password to be unlocked
-                // - the device is not locked
-                if ((mKeyguardManager.isKeyguardLocked() && !mKeyguardManager.isKeyguardSecure())
-                        || !mKeyguardManager.isKeyguardLocked()) {
-                    mContext.startActivity(voiceIntent);
-                }
+            if (voiceIntent != null) {
+                voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                mContext.startActivity(voiceIntent);
             }
         } catch (ActivityNotFoundException e) {
-            Log.e(TAG, "Error launching activity for ACTION_WEB_SEARCH: " + e);
+            Log.w(TAG, "No activity for search: " + e);
         } finally {
             if (needWakeLock) {
                 mMediaEventWakeLock.release();
@@ -3843,20 +3852,6 @@
         }
     }
 
-    /**
-     * Verify whether it is safe to start voice-based interactions given the state of the system
-     * @return false is the Keyguard is locked and secure, true otherwise
-     */
-    private boolean safeToStartVoiceBasedInteractions() {
-        KeyguardManager keyguard =
-                (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        if (keyguard == null) {
-            return false;
-        }
-        
-        return true;
-    }
-
     private PowerManager.WakeLock mMediaEventWakeLock;
 
     private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
index 3657d8a..d8aa40f 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
@@ -376,8 +376,6 @@
 
     @Override
     public void process(FilterContext context) {
-        if (mLogVerbose) Log.v(TAG, "Starting frame processing");
-
         GLEnvironment glEnv = context.getGLEnvironment();
         // Get input frame
         Frame input = pullInput("videoframe");
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
index b023e42..674a2bd 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
@@ -121,6 +121,7 @@
     }
 
     public void updateRenderMode() {
+        if (mLogVerbose) Log.v(TAG, "updateRenderMode. Thread: " + Thread.currentThread());
         if (mRenderModeString != null) {
             if (mRenderModeString.equals("stretch")) {
                 mRenderMode = RENDERMODE_STRETCH;
@@ -139,6 +140,7 @@
 
     @Override
     public void prepare(FilterContext context) {
+        if (mLogVerbose) Log.v(TAG, "Prepare. Thread: " + Thread.currentThread());
         // Create identity shader to render, and make sure to render upside-down, as textures
         // are stored internally bottom-to-top.
         mProgram = ShaderProgram.createIdentity(context);
@@ -214,8 +216,10 @@
         float currentAspectRatio =
           (float)input.getFormat().getWidth() / input.getFormat().getHeight();
         if (currentAspectRatio != mAspectRatio) {
-            if (mLogVerbose) Log.v(TAG, "New aspect ratio: " + currentAspectRatio +
-                ", previously: " + mAspectRatio);
+            if (mLogVerbose) {
+                Log.v(TAG, "Process. New aspect ratio: " + currentAspectRatio +
+                    ", previously: " + mAspectRatio + ". Thread: " + Thread.currentThread());
+            }
             mAspectRatio = currentAspectRatio;
             updateTargetRect();
         }
@@ -249,6 +253,7 @@
 
     @Override
     public void fieldPortValueUpdated(String name, FilterContext context) {
+        if (mLogVerbose) Log.v(TAG, "FPVU. Thread: " + Thread.currentThread());
         updateRenderMode();
     }
 
@@ -260,16 +265,22 @@
     }
 
     private void updateTargetRect() {
+        if (mLogVerbose) Log.v(TAG, "updateTargetRect. Thread: " + Thread.currentThread());
         if (mScreenWidth > 0 && mScreenHeight > 0 && mProgram != null) {
             float screenAspectRatio = (float)mScreenWidth / mScreenHeight;
             float relativeAspectRatio = screenAspectRatio / mAspectRatio;
+            if (mLogVerbose) {
+                Log.v(TAG, "UTR. screen w = " + (float)mScreenWidth + " x screen h = " +
+                    (float)mScreenHeight + " Screen AR: " + screenAspectRatio +
+                    ", frame AR: "  + mAspectRatio + ", relative AR: " + relativeAspectRatio);
+            }
 
             if (relativeAspectRatio == 1.0f && mRenderMode != RENDERMODE_CUSTOMIZE) {
+                mProgram.setTargetRect(0, 0, 1, 1);
                 mProgram.setClearsOutput(false);
             } else {
                 switch (mRenderMode) {
                     case RENDERMODE_STRETCH:
-                        mProgram.setTargetRect(0, 0, 1, 1);
                         mTargetQuad.p0.set(0f, 0.0f);
                         mTargetQuad.p1.set(1f, 0.0f);
                         mTargetQuad.p2.set(0f, 1.0f);
@@ -313,6 +324,7 @@
                         ((ShaderProgram) mProgram).setSourceRegion(mSourceQuad);
                         break;
                 }
+                if (mLogVerbose) Log.v(TAG,  "UTR. quad: " + mTargetQuad);
                 ((ShaderProgram) mProgram).setTargetRegion(mTargetQuad);
             }
         }
diff --git a/packages/SystemUI/res/anim/search_launch_enter.xml b/packages/SystemUI/res/anim/search_launch_enter.xml
new file mode 100644
index 0000000..055ea5d
--- /dev/null
+++ b/packages/SystemUI/res/anim/search_launch_enter.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:shareInterpolator="false" android:zAdjustment="top">
+
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+            android:interpolator="@android:interpolator/decelerate_quad"
+            android:duration="300"/>
+
+    <translate android:fromYDelta="200" android:toYDelta="0"
+            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+            android:interpolator="@android:interpolator/decelerate_cubic"
+            android:duration="300" />
+</set>
diff --git a/packages/SystemUI/res/anim/search_launch_exit.xml b/packages/SystemUI/res/anim/search_launch_exit.xml
new file mode 100644
index 0000000..b4ed278
--- /dev/null
+++ b/packages/SystemUI/res/anim/search_launch_exit.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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.
+*/
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+       android:interpolator="@android:anim/accelerate_interpolator"
+       android:fromXDelta="0" android:toXDelta="0"
+       android:duration="300" />
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index 6b0bb87..28283ef4 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -18,6 +18,8 @@
 
 import android.animation.Animator;
 import android.animation.LayoutTransition;
+import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
 import android.app.SearchManager;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
@@ -36,6 +38,7 @@
 
 import com.android.internal.widget.multiwaveview.MultiWaveView;
 import com.android.internal.widget.multiwaveview.MultiWaveView.OnTriggerListener;
+import com.android.server.am.ActivityManagerService;
 import com.android.systemui.R;
 import com.android.systemui.recent.StatusBarTouchProxy;
 import com.android.systemui.statusbar.BaseStatusBar;
@@ -103,26 +106,22 @@
     }
 
     private void startAssistActivity() {
-        if (mSearchManager != null) {
-            ComponentName globalSearchActivity = mSearchManager.getGlobalSearchActivity();
-            if (globalSearchActivity != null) {
-                Intent intent = new Intent(Intent.ACTION_ASSIST);
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                intent.setPackage(globalSearchActivity.getPackageName());
-                try {
-                    mContext.startActivity(intent);
-                } catch (ActivityNotFoundException e) {
-                    Slog.w(TAG, "Activity not found for " + intent.getAction());
-                }
-            } else {
-                Slog.w(TAG, "No global search activity");
-            }
+        Intent intent = getAssistIntent();
+        try {
+            ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
+                    R.anim.search_launch_enter, R.anim.search_launch_exit);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mContext.startActivity(intent, opts.toBundle());
+        } catch (ActivityNotFoundException e) {
+            Slog.w(TAG, "Activity not found for " + intent.getAction());
         }
     }
 
     final MultiWaveView.OnTriggerListener mMultiWaveViewListener
             = new MultiWaveView.OnTriggerListener() {
 
+        private int mTarget = -1;
+
         public void onGrabbed(View v, int handle) {
         }
 
@@ -136,11 +135,18 @@
         }
 
         public void onTrigger(View v, int target) {
-            final int resId = mMultiWaveView.getResourceIdForTarget(target);
-            switch (resId) {
-                case com.android.internal.R.drawable.ic_lockscreen_search:
-                    startAssistActivity();
-                break;
+            mTarget = target;
+        }
+
+        public void onFinishFinalAnimation() {
+            if (mTarget != -1) {
+                final int resId = mMultiWaveView.getResourceIdForTarget(mTarget);
+                mTarget = -1; // a safety to make sure we never launch w/o prior call to onTrigger
+                switch (resId) {
+                    case com.android.internal.R.drawable.ic_lockscreen_search:
+                        startAssistActivity();
+                    break;
+                }
             }
             mBar.hideSearchPanel();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 69d2e73..d38611d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -515,14 +515,8 @@
         public boolean onTouch(View v, MotionEvent event) {
             switch(event.getAction()) {
                 case MotionEvent.ACTION_DOWN:
-                    Slog.d(TAG, "showing search panel");
                     showSearchPanel();
                 break;
-
-                case MotionEvent.ACTION_UP:
-                    Slog.d(TAG, "hiding search panel");
-                    hideSearchPanel();
-                break;
             }
             return false;
         }
@@ -533,8 +527,8 @@
 
         mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
         mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
+        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
         updateSearchPanel();
-//        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
     }
 
     // For small-screen devices (read: phones) that lack hardware navigation buttons
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index dba1606..10c5dd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -188,6 +188,17 @@
 
     public Context getContext() { return mContext; }
 
+    private View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
+        public boolean onTouch(View v, MotionEvent event) {
+            switch(event.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    showSearchPanel();
+                break;
+            }
+            return false;
+        }
+    };
+
     @Override
     protected void createAndAddWindows() {
         addStatusBarWindow();
@@ -290,6 +301,7 @@
 
         // Search Panel
         mStatusBarView.setBar(this);
+        mHomeButton.setOnTouchListener(mHomeSearchActionListener);
         updateSearchPanel();
 
         // Input methods Panel
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index 22f70a5..30cb530 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -401,6 +401,10 @@
         public void cleanUp() {
             mMultiWaveView.setOnTriggerListener(null);
         }
+
+        public void onFinishFinalAnimation() {
+
+        }
     }
 
     private void requestUnlockScreen() {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index cee01ac..29de5c1 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -326,6 +326,7 @@
 
     RecentApplicationsDialog mRecentAppsDialog;
     int mRecentAppsDialogHeldModifiers;
+    boolean mLanguageSwitchKeyPressed;
 
     int mLidState = LID_ABSENT;
     boolean mHaveBuiltInKeyboard;
@@ -1943,6 +1944,22 @@
                     RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH);
         }
 
+        // Handle keyboard language switching.
+        if (down && repeatCount == 0
+                && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH
+                        || (keyCode == KeyEvent.KEYCODE_SPACE
+                                && (metaState & KeyEvent.META_CTRL_MASK) != 0))) {
+            int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
+            mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
+            return -1;
+        }
+        if (mLanguageSwitchKeyPressed && !down
+                && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH
+                        || keyCode == KeyEvent.KEYCODE_SPACE)) {
+            mLanguageSwitchKeyPressed = false;
+            return -1;
+        }
+
         // Let the application handle the key.
         return 0;
     }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index a49ccf7..ff14568 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -195,6 +195,7 @@
     private PendingIntent mImeSwitchPendingIntent;
     private boolean mShowOngoingImeSwitcherForPhones;
     private boolean mNotificationShown;
+    private final boolean mImeSelectedOnBoot;
 
     class SessionState {
         final ClientState client;
@@ -590,7 +591,6 @@
         mImeSwitcherNotification.vibrate = null;
         Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
         mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
-        mLastSystemLocale = mRes.getConfiguration().locale;
 
         mShowOngoingImeSwitcherForPhones = false;
 
@@ -612,11 +612,17 @@
         // mSettings should be created before buildInputMethodListLocked
         mSettings = new InputMethodSettings(
                 mRes, context.getContentResolver(), mMethodMap, mMethodList);
+
+        // Just checking if defaultImiId is empty or not
+        final String defaultImiId = Settings.Secure.getString(
+                mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+        mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
+
         buildInputMethodListLocked(mMethodList, mMethodMap);
         mSettings.enableAllIMEsIfThereIsNoEnabledIME();
 
-        if (TextUtils.isEmpty(Settings.Secure.getString(
-                mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD))) {
+        if (!mImeSelectedOnBoot) {
+            Slog.w(TAG, "No IME selected. Choose the most applicable IME.");
             resetDefaultImeLocked(context);
         }
 
@@ -639,6 +645,10 @@
     }
 
     private void checkCurrentLocaleChangedLocked() {
+        if (!mSystemReady) {
+            // not system ready
+            return;
+        }
         final Locale newLocale = mRes.getConfiguration().locale;
         if (newLocale != null && !newLocale.equals(mLastSystemLocale)) {
             if (DEBUG) {
@@ -647,6 +657,7 @@
             buildInputMethodListLocked(mMethodList, mMethodMap);
             // Reset the current ime to the proper one
             resetDefaultImeLocked(mContext);
+            updateFromSettingsLocked();
             mLastSystemLocale = newLocale;
         }
     }
@@ -675,7 +686,10 @@
         }
     }
 
-    private static boolean isValidSystemDefaultIme(InputMethodInfo imi, Context context) {
+    private boolean isValidSystemDefaultIme(InputMethodInfo imi, Context context) {
+        if (!mSystemReady) {
+            return false;
+        }
         if (!isSystemIme(imi)) {
             return false;
         }
@@ -738,7 +752,6 @@
                         mContext.getSystemService(Context.KEYGUARD_SERVICE);
                 mNotificationManager = (NotificationManager)
                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-                mLastSystemLocale = mContext.getResources().getConfiguration().locale;
                 mStatusBar = statusBar;
                 statusBar.setIconVisibility("ime", false);
                 updateImeWindowStatusLocked();
@@ -748,6 +761,12 @@
                     mWindowManagerService.setOnHardKeyboardStatusChangeListener(
                             mHardKeyboardListener);
                 }
+                buildInputMethodListLocked(mMethodList, mMethodMap);
+                if (!mImeSelectedOnBoot) {
+                    Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here.");
+                    checkCurrentLocaleChangedLocked();
+                }
+                mLastSystemLocale = mRes.getConfiguration().locale;
                 try {
                     startInputInnerLocked();
                 } catch (RuntimeException e) {
@@ -1421,34 +1440,41 @@
             throw new IllegalArgumentException("Unknown id: " + id);
         }
 
+        // See if we need to notify a subtype change within the same IME.
         if (id.equals(mCurMethodId)) {
-            InputMethodSubtype subtype = null;
-            if (subtypeId >= 0 && subtypeId < info.getSubtypeCount()) {
-                subtype = info.getSubtypeAt(subtypeId);
+            final int subtypeCount = info.getSubtypeCount();
+            if (subtypeCount <= 0) {
+                return;
             }
-            if (subtype != mCurrentSubtype) {
-                synchronized (mMethodMap) {
-                    if (subtype != null) {
-                        setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
-                    }
-                    if (mCurMethod != null) {
-                        try {
-                            refreshImeWindowVisibilityLocked();
-                            // If subtype is null, try to find the most applicable one from
-                            // getCurrentInputMethodSubtype.
-                            if (subtype == null) {
-                                subtype = getCurrentInputMethodSubtype();
-                            }
-                            mCurMethod.changeInputMethodSubtype(subtype);
-                        } catch (RemoteException e) {
-                            return;
-                        }
+            final InputMethodSubtype oldSubtype = mCurrentSubtype;
+            final InputMethodSubtype newSubtype;
+            if (subtypeId >= 0 && subtypeId < subtypeCount) {
+                newSubtype = info.getSubtypeAt(subtypeId);
+            } else {
+                // If subtype is null, try to find the most applicable one from
+                // getCurrentInputMethodSubtype.
+                newSubtype = getCurrentInputMethodSubtype();
+            }
+            if (newSubtype == null || oldSubtype == null) {
+                Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype
+                        + ", new subtype = " + newSubtype);
+                return;
+            }
+            if (newSubtype != oldSubtype) {
+                setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
+                if (mCurMethod != null) {
+                    try {
+                        refreshImeWindowVisibilityLocked();
+                        mCurMethod.changeInputMethodSubtype(newSubtype);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed to call changeInputMethodSubtype");
                     }
                 }
             }
             return;
         }
 
+        // Changing to a different IME.
         final long ident = Binder.clearCallingIdentity();
         try {
             // Set a subtype to this input method.
@@ -2137,7 +2163,6 @@
         return subtypes;
     }
 
-
     private static ArrayList<InputMethodSubtype> getOverridingImplicitlyEnabledSubtypes(
             InputMethodInfo imi, String mode) {
         ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
@@ -2155,15 +2180,19 @@
         List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
         if (enabled != null && enabled.size() > 0) {
             // We'd prefer to fall back on a system IME, since that is safer.
-            int i=enabled.size();
+            int i = enabled.size();
+            int firstFoundSystemIme = -1;
             while (i > 0) {
                 i--;
                 final InputMethodInfo imi = enabled.get(i);
-                if (isSystemIme(imi) && !imi.isAuxiliaryIme()) {
-                    break;
+                if (isSystemImeThatHasEnglishSubtype(imi) && !imi.isAuxiliaryIme()) {
+                    return imi;
+                }
+                if (firstFoundSystemIme < 0 && isSystemIme(imi) && !imi.isAuxiliaryIme()) {
+                    firstFoundSystemIme = i;
                 }
             }
-            return enabled.get(i);
+            return enabled.get(Math.max(firstFoundSystemIme, 0));
         }
         return null;
     }
@@ -2238,11 +2267,17 @@
             }
         }
 
-        String defaultIme = Settings.Secure.getString(mContext
+        final String defaultImiId = Settings.Secure.getString(mContext
                 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
-        if (!TextUtils.isEmpty(defaultIme) && !map.containsKey(defaultIme)) {
-            if (chooseNewDefaultIMELocked()) {
-                updateFromSettingsLocked();
+        if (!TextUtils.isEmpty(defaultImiId)) {
+            if (!map.containsKey(defaultImiId)) {
+                Slog.w(TAG, "Default IME is uninstalled. Choose new default IME.");
+                if (chooseNewDefaultIMELocked()) {
+                    updateFromSettingsLocked();
+                }
+            } else {
+                // Double check that the default IME is certainly enabled.
+                setInputMethodEnabledLocked(defaultImiId, true);
             }
         }
     }
@@ -2626,7 +2661,8 @@
                 mCurrentSubtype = subtype;
             } else {
                 mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
-                mCurrentSubtype = null;
+                // If the subtype is not specified, choose the most applicable one
+                mCurrentSubtype = getCurrentInputMethodSubtype();
             }
         }
 
@@ -3007,8 +3043,8 @@
             mContext = context;
             mPm = context.getPackageManager();
             mImms = imms;
-            mSystemLocaleStr =
-                    imms.mLastSystemLocale != null ? imms.mLastSystemLocale.toString() : "";
+            final Locale locale = context.getResources().getConfiguration().locale;
+            mSystemLocaleStr = locale != null ? locale.toString() : "";
         }
 
         private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis =
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 3fbac38..3e35b20d 100644
--- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -16,7 +16,6 @@
 
 package com.android.server.accessibility;
 
-import com.android.server.accessibility.TouchExplorer.GestureListener;
 import com.android.server.input.InputFilter;
 
 import android.content.Context;
@@ -40,7 +39,7 @@
 
     private final PowerManager mPm;
 
-    private final GestureListener mGestureListener;
+    private final AccessibilityManagerService mAms;
 
     /**
      * This is an interface for explorers that take a {@link MotionEvent}
@@ -73,10 +72,10 @@
 
     private int mTouchscreenSourceDeviceId;
 
-    public AccessibilityInputFilter(Context context, GestureListener gestureListener) {
+    public AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
         super(context.getMainLooper());
         mContext = context;
-        mGestureListener = gestureListener;
+        mAms = service;
         mPm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
     }
 
@@ -85,7 +84,7 @@
         if (DEBUG) {
             Slog.d(TAG, "Accessibility input filter installed.");
         }
-        mTouchExplorer = new TouchExplorer(this, mContext, mGestureListener);
+        mTouchExplorer = new TouchExplorer(this, mContext, mAms);
         super.onInstalled();
     }
 
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index f23b25e..ebc2074 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -24,12 +24,15 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.app.AlertDialog;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
@@ -58,7 +61,9 @@
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -66,8 +71,8 @@
 import android.view.accessibility.IAccessibilityManager;
 import android.view.accessibility.IAccessibilityManagerClient;
 
+import com.android.internal.R;
 import com.android.internal.content.PackageMonitor;
-import com.android.server.accessibility.TouchExplorer.GestureListener;
 import com.android.server.wm.WindowManagerService;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -90,8 +95,7 @@
  *
  * @hide
  */
-public class AccessibilityManagerService extends IAccessibilityManager.Stub
-        implements GestureListener {
+public class AccessibilityManagerService extends IAccessibilityManager.Stub {
 
     private static final boolean DEBUG = false;
 
@@ -102,6 +106,8 @@
 
     private static final int OWN_PROCESS_ID = android.os.Process.myPid();
 
+    private static final int MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG = 1;
+
     private static int sIdCounter = 0;
 
     private static int sNextWindowId;
@@ -128,6 +134,8 @@
 
     private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
 
+    private final Rect mTempRect = new Rect();
+
     private PackageManager mPackageManager;
 
     private int mHandledFeedbackTypes = 0;
@@ -146,23 +154,15 @@
 
     private final SecurityPolicy mSecurityPolicy;
 
+    private final MainHanler mMainHandler;
+
     private Service mUiAutomationService;
 
-    /**
-     * Handler for delayed event dispatch.
-     */
-    private Handler mHandler = new Handler() {
+    private Service mQueryBridge;
 
-        @Override
-        public void handleMessage(Message message) {
-            Service service = (Service) message.obj;
-            int eventType = message.arg1;
+    private boolean mTouchExplorationGestureEnded;
 
-            synchronized (mLock) {
-                notifyAccessibilityEventLocked(service, eventType);
-            }
-        }
-    };
+    private boolean mTouchExplorationGestureStarted;
 
     /**
      * Creates a new instance.
@@ -175,7 +175,7 @@
         mWindowManagerService = (WindowManagerService) ServiceManager.getService(
                 Context.WINDOW_SERVICE);
         mSecurityPolicy = new SecurityPolicy();
-
+        mMainHandler = new MainHanler();
         registerPackageChangeAndBootCompletedBroadcastReceiver();
         registerSettingsContentObservers();
     }
@@ -349,15 +349,37 @@
     }
 
     public boolean sendAccessibilityEvent(AccessibilityEvent event) {
+        // The event for gesture start should be strictly before the
+        // first hover enter event for the gesture.
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
+                && mTouchExplorationGestureStarted) {
+            mTouchExplorationGestureStarted = false;
+            AccessibilityEvent gestureStartEvent = AccessibilityEvent.obtain(
+                    AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
+            sendAccessibilityEvent(gestureStartEvent);
+        }
+
         synchronized (mLock) {
             if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) {
-                mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event);
+                mSecurityPolicy.updateActiveWindowAndEventSourceLocked(event);
                 notifyAccessibilityServicesDelayedLocked(event, false);
                 notifyAccessibilityServicesDelayedLocked(event, true);
             }
         }
+
+        // The event for gesture end should be strictly after the
+        // last hover exit event for the gesture.
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+                && mTouchExplorationGestureEnded) {
+            mTouchExplorationGestureEnded = false;
+            AccessibilityEvent gestureEndEvent = AccessibilityEvent.obtain(
+                    AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
+            sendAccessibilityEvent(gestureEndEvent);
+        }
+
         event.recycle();
         mHandledFeedbackTypes = 0;
+
         return (OWN_PROCESS_ID != Binder.getCallingPid());
     }
 
@@ -472,8 +494,7 @@
         }
     }
 
-    @Override
-    public boolean onGesture(int gestureId) {
+    boolean onGesture(int gestureId) {
         synchronized (mLock) {
             boolean handled = notifyGestureLocked(gestureId, false);
             if (!handled) {
@@ -483,6 +504,65 @@
         }
     }
 
+    /**
+     * Gets the bounds of the accessibility focus if the provided,
+     * point coordinates are within the currently active window
+     * and accessibility focus is found within the latter.
+     *
+     * @param x X coordinate.
+     * @param y Y coordinate
+     * @param outBounds The output to which to write the focus bounds.
+     * @return The accessibility focus rectangle if the point is in the
+     *     window and the window has accessibility focus.
+     */
+    boolean getAccessibilityFocusBounds(int x, int y, Rect outBounds) {
+        // Instead of keeping track of accessibility focus events per
+        // window to be able to find the focus in the active window,
+        // we take a stateless approach and look it up. This is fine
+        // since we do this only when the user clicks/long presses.
+        Service service = getQueryBridge();
+        final int connectionId = service.mId;
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        client.addConnection(connectionId, service);
+        try {
+            AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
+                    .getRootInActiveWindow(connectionId);
+            if (root == null) {
+                return false;
+            }
+            Rect bounds = mTempRect;
+            root.getBoundsInScreen(bounds);
+            if (!bounds.contains(x, y)) {
+                return false;
+            }
+            AccessibilityNodeInfo focus = root.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
+            if (focus == null) {
+                return false;
+            }
+            focus.getBoundsInScreen(outBounds);
+            return true;
+        } finally {
+            client.removeConnection(connectionId);
+        }
+    }
+
+    private Service getQueryBridge() {
+        if (mQueryBridge == null) {
+            AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+            mQueryBridge = new Service(null, info, true);
+        }
+        return mQueryBridge;
+    }
+
+    public void touchExplorationGestureEnded() {
+        mTouchExplorationGestureEnded = true;
+    }
+
+    public void touchExplorationGestureStarted() {
+        mTouchExplorationGestureStarted = true;
+    }
+
     private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
         // TODO: Now we are giving the gestures to the last enabled
         //       service that can handle them which is the last one
@@ -496,12 +576,7 @@
         for (int i = mServices.size() - 1; i >= 0; i--) {
             Service service = mServices.get(i);
             if (service.mReqeustTouchExplorationMode && service.mIsDefault == isDefault) {
-                try {
-                    service.mServiceInterface.onGesture(gestureId);
-                } catch (RemoteException re) {
-                    Slog.e(LOG_TAG, "Error during sending gesture " + gestureId
-                            + " to " + service.mService, re);
-                }
+                service.notifyGesture(gestureId);
                 return true;
             }
         }
@@ -573,7 +648,7 @@
                 if (service.mIsDefault == isDefault) {
                     if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
                         mHandledFeedbackTypes |= service.mFeedbackType;
-                        notifyAccessibilityServiceDelayedLocked(service, event);
+                        service.notifyAccessibilityEvent(event);
                     }
                 }
             }
@@ -586,90 +661,6 @@
     }
 
     /**
-     * Performs an {@link AccessibilityService} delayed notification. The delay is configurable
-     * and denotes the period after the last event before notifying the service.
-     *
-     * @param service The service.
-     * @param event The event.
-     */
-    private void notifyAccessibilityServiceDelayedLocked(Service service,
-            AccessibilityEvent event) {
-        synchronized (mLock) {
-            final int eventType = event.getEventType();
-            // Make a copy since during dispatch it is possible the event to
-            // be modified to remove its source if the receiving service does
-            // not have permission to access the window content.
-            AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
-            AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
-            service.mPendingEvents.put(eventType, newEvent);
-
-            final int what = eventType | (service.mId << 16);
-            if (oldEvent != null) {
-                mHandler.removeMessages(what);
-                oldEvent.recycle();
-            }
-
-            Message message = mHandler.obtainMessage(what, service);
-            message.arg1 = eventType;
-            mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
-        }
-    }
-
-    /**
-     * Notifies an accessibility service client for a scheduled event given the event type.
-     *
-     * @param service The service client.
-     * @param eventType The type of the event to dispatch.
-     */
-    private void notifyAccessibilityEventLocked(Service service, int eventType) {
-        IAccessibilityServiceClient listener = service.mServiceInterface;
-
-        // If the service died/was disabled while the message for dispatching
-        // the accessibility event was propagating the listener may be null.
-        if (listener == null) {
-            return;
-        }
-
-        AccessibilityEvent event = service.mPendingEvents.get(eventType);
-
-        // Check for null here because there is a concurrent scenario in which this
-        // happens: 1) A binder thread calls notifyAccessibilityServiceDelayedLocked
-        // which posts a message for dispatching an event. 2) The message is pulled
-        // from the queue by the handler on the service thread and the latter is
-        // just about to acquire the lock and call this method. 3) Now another binder
-        // thread acquires the lock calling notifyAccessibilityServiceDelayedLocked
-        // so the service thread waits for the lock; 4) The binder thread replaces
-        // the event with a more recent one (assume the same event type) and posts a
-        // dispatch request releasing the lock. 5) Now the main thread is unblocked and
-        // dispatches the event which is removed from the pending ones. 6) And ... now
-        // the service thread handles the last message posted by the last binder call
-        // but the event is already dispatched and hence looking it up in the pending
-        // ones yields null. This check is much simpler that keeping count for each
-        // event type of each service to catch such a scenario since only one message
-        // is processed at a time.
-        if (event == null) {
-            return;
-        }
-
-        service.mPendingEvents.remove(eventType);
-        try {
-            if (mSecurityPolicy.canRetrieveWindowContent(service)) {
-                event.setConnectionId(service.mId);
-            } else {
-                event.setSource(null);
-            }
-            event.setSealed(true);
-            listener.onAccessibilityEvent(event);
-            event.recycle();
-            if (DEBUG) {
-                Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
-            }
-        } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
-        }
-    }
-
-    /**
      * Adds a service.
      *
      * @param service The service to add.
@@ -683,6 +674,7 @@
             mServices.add(service);
             mComponentNameToServiceMap.put(service.mComponentName, service);
             updateInputFilterLocked();
+            tryEnableTouchExploration(service);
         } catch (RemoteException e) {
             /* do nothing */
         }
@@ -700,10 +692,10 @@
             return false;
         }
         mComponentNameToServiceMap.remove(service.mComponentName);
-        mHandler.removeMessages(service.mId);
         service.unlinkToOwnDeath();
         service.dispose();
         updateInputFilterLocked();
+        tryDisableTouchExploration(service);
         return removed;
     }
 
@@ -932,6 +924,29 @@
                 Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
     }
 
+    private void tryEnableTouchExploration(final Service service) {
+        if (!mIsTouchExplorationEnabled && service.mRequestTouchExplorationMode) {
+            mMainHandler.obtainMessage(MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG,
+                    service).sendToTarget();
+        }
+    }
+
+    private void tryDisableTouchExploration(Service service) {
+        if (mIsTouchExplorationEnabled && service.mReqeustTouchExplorationMode) {
+            synchronized (mLock) {
+                final int serviceCount = mServices.size();
+                for (int i = 0; i < serviceCount; i++) {
+                    Service other = mServices.get(i);
+                    if (other != service && other.mRequestTouchExplorationMode) {
+                        return;
+                    }
+                }
+                Settings.Secure.putInt(mContext.getContentResolver(),
+                        Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0);
+            }
+        }
+    }
+
     private class AccessibilityConnectionWrapper implements DeathRecipient {
         private final int mWindowId;
         private final IAccessibilityInteractionConnection mConnection;
@@ -959,6 +974,42 @@
         }
     }
 
+    private class MainHanler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            final int type = msg.what;
+            switch (type) {
+                case MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG: {
+                    Service service = (Service) msg.obj;
+                    String label = service.mResolveInfo.loadLabel(
+                            mContext.getPackageManager()).toString();
+                    final AlertDialog dialog = new AlertDialog.Builder(mContext)
+                        .setIcon(android.R.drawable.ic_dialog_alert)
+                        .setPositiveButton(android.R.string.ok, new OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                Settings.Secure.putInt(mContext.getContentResolver(),
+                                        Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1);
+                            }
+                        })
+                        .setNegativeButton(android.R.string.cancel, new OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                dialog.dismiss();
+                            }
+                        })
+                        .setTitle(R.string.enable_explore_by_touch_warning_title)
+                        .setMessage(mContext.getString(
+                            R.string.enable_explore_by_touch_warning_message, label))
+                        .create();
+                    dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
+                    dialog.setCanceledOnTouchOutside(true);
+                    dialog.show();
+                }
+            }
+        }
+    }
+
     /**
      * This class represents an accessibility service. It stores all per service
      * data required for the service management, provides API for starting/stopping the
@@ -969,6 +1020,11 @@
      */
     class Service extends IAccessibilityServiceConnection.Stub
             implements ServiceConnection, DeathRecipient {
+
+        // We pick the MSB to avoid collision since accessibility event types are
+        // used as message types allowing us to remove messages per event type. 
+        private static final int MSG_ON_GESTURE = 0x80000000;
+
         int mId = 0;
 
         AccessibilityServiceInfo mAccessibilityServiceInfo;
@@ -985,6 +1041,8 @@
 
         boolean mIsDefault;
 
+        boolean mRequestTouchExplorationMode;
+
         boolean mIncludeNotImportantViews;
 
         long mNotificationTimeout;
@@ -1001,12 +1059,35 @@
 
         final Rect mTempBounds = new Rect();
 
+        final ResolveInfo mResolveInfo;
+
         // the events pending events to be dispatched to this service
         final SparseArray<AccessibilityEvent> mPendingEvents =
             new SparseArray<AccessibilityEvent>();
 
+        /**
+         * Handler for delayed event dispatch.
+         */
+        public Handler mHandler = new Handler() {
+            @Override
+            public void handleMessage(Message message) {
+                final int type = message.what;
+                switch (type) {
+                    case MSG_ON_GESTURE: {
+                        final int gestureId = message.arg1;
+                        notifyGestureInternal(gestureId);
+                    } break;
+                    default: {
+                        final int eventType = type;
+                        notifyAccessibilityEventInternal(eventType);
+                    } break;
+                }
+            }
+        };
+
         public Service(ComponentName componentName,
                 AccessibilityServiceInfo accessibilityServiceInfo, boolean isAutomation) {
+            mResolveInfo = accessibilityServiceInfo.getResolveInfo();
             mId = sIdCounter++;
             mComponentName = componentName;
             mAccessibilityServiceInfo = accessibilityServiceInfo;
@@ -1043,6 +1124,9 @@
                     (info.flags & FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
             }
 
+            mRequestTouchExplorationMode = (info.flags
+                    & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
+
             synchronized (mLock) {
                 tryAddServiceLocked(this);
             }
@@ -1403,6 +1487,108 @@
             }
         }
 
+        /**
+         * Performs a notification for an {@link AccessibilityEvent}.
+         *
+         * @param event The event.
+         */
+        public void notifyAccessibilityEvent(AccessibilityEvent event) {
+            synchronized (mLock) {
+                final int eventType = event.getEventType();
+                // Make a copy since during dispatch it is possible the event to
+                // be modified to remove its source if the receiving service does
+                // not have permission to access the window content.
+                AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
+                AccessibilityEvent oldEvent = mPendingEvents.get(eventType);
+                mPendingEvents.put(eventType, newEvent);
+
+                final int what = eventType;
+                if (oldEvent != null) {
+                    mHandler.removeMessages(what);
+                    oldEvent.recycle();
+                }
+
+                Message message = mHandler.obtainMessage(what);
+                mHandler.sendMessageDelayed(message, mNotificationTimeout);
+            }
+        }
+
+        /**
+         * Notifies an accessibility service client for a scheduled event given the event type.
+         *
+         * @param eventType The type of the event to dispatch.
+         */
+        private void notifyAccessibilityEventInternal(int eventType) {
+            IAccessibilityServiceClient listener;
+            AccessibilityEvent event;
+
+            synchronized (mLock) {
+                listener = mServiceInterface;
+
+                // If the service died/was disabled while the message for dispatching
+                // the accessibility event was propagating the listener may be null.
+                if (listener == null) {
+                    return;
+                }
+
+                event = mPendingEvents.get(eventType);
+
+                // Check for null here because there is a concurrent scenario in which this
+                // happens: 1) A binder thread calls notifyAccessibilityServiceDelayedLocked
+                // which posts a message for dispatching an event. 2) The message is pulled
+                // from the queue by the handler on the service thread and the latter is
+                // just about to acquire the lock and call this method. 3) Now another binder
+                // thread acquires the lock calling notifyAccessibilityServiceDelayedLocked
+                // so the service thread waits for the lock; 4) The binder thread replaces
+                // the event with a more recent one (assume the same event type) and posts a
+                // dispatch request releasing the lock. 5) Now the main thread is unblocked and
+                // dispatches the event which is removed from the pending ones. 6) And ... now
+                // the service thread handles the last message posted by the last binder call
+                // but the event is already dispatched and hence looking it up in the pending
+                // ones yields null. This check is much simpler that keeping count for each
+                // event type of each service to catch such a scenario since only one message
+                // is processed at a time.
+                if (event == null) {
+                    return;
+                }
+
+                mPendingEvents.remove(eventType);
+                if (mSecurityPolicy.canRetrieveWindowContent(this)) {
+                    event.setConnectionId(mId);
+                } else {
+                    event.setSource(null);
+                }
+                event.setSealed(true);
+            }
+
+            try {
+                listener.onAccessibilityEvent(event);
+                if (DEBUG) {
+                    Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
+                }
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re);
+            } finally {
+                event.recycle();
+            }
+        }
+
+        public void notifyGesture(int gestureId) {
+            mHandler.obtainMessage(MSG_ON_GESTURE, gestureId, 0).sendToTarget();
+        }
+
+        private void notifyGestureInternal(int gestureId) {
+            IAccessibilityServiceClient listener = mServiceInterface;
+            if (listener != null) {
+                try {
+                    listener.onGesture(gestureId);
+                } catch (RemoteException re) {
+                    Slog.e(LOG_TAG, "Error during sending gesture " + gestureId
+                            + " to " + mService, re);
+                }
+            }
+        }
+
         private void sendDownAndUpKeyEvents(int keyCode) {
             final long token = Binder.clearCallingIdentity();
 
@@ -1454,7 +1640,7 @@
 
         private int resolveAccessibilityWindowId(int accessibilityWindowId) {
             if (accessibilityWindowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) {
-                return mSecurityPolicy.mRetrievalAlowingWindowId;
+                return mSecurityPolicy.mActiveWindowId;
             }
             return accessibilityWindowId;
         }
@@ -1497,24 +1683,35 @@
             | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
             | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
 
-        private static final int RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES =
-            AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
-            | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
-            | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT;
-
-        private int mRetrievalAlowingWindowId;
+        private int mActiveWindowId;
 
         private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) {
             // Send window changed event only for the retrieval allowing window.
             return (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
-                    || event.getWindowId() == mRetrievalAlowingWindowId);
+                    || event.getWindowId() == mActiveWindowId);
         }
 
-        public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) {
+        public void updateActiveWindowAndEventSourceLocked(AccessibilityEvent event) {
+            // The active window is either the window that has input focus or
+            // the window that the user is currently touching. If the user is
+            // touching a window that does not have input focus as soon as the
+            // the user stops touching that window the focused window becomes
+            // the active one.
             final int windowId = event.getWindowId();
             final int eventType = event.getEventType();
-            if ((eventType & RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES) != 0) {
-                mRetrievalAlowingWindowId = windowId;
+            switch (eventType) {
+                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
+                    if (getFocusedWindowId() == windowId) {
+                        mActiveWindowId = windowId;
+                    }
+                } break;
+                case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
+                case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
+                    mActiveWindowId = windowId;
+                } break;
+                case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: {
+                    mActiveWindowId = getFocusedWindowId();
+                } break;
             }
             if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) {
                 event.setSource(null);
@@ -1522,7 +1719,7 @@
         }
 
         public int getRetrievalAllowingWindowLocked() {
-            return mRetrievalAlowingWindowId;
+            return mActiveWindowId;
         }
 
         public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
@@ -1550,7 +1747,7 @@
         }
 
         private boolean isRetrievalAllowingWindow(int windowId) {
-            return (mRetrievalAlowingWindowId == windowId);
+            return (mActiveWindowId == windowId);
         }
 
         private boolean isActionPermitted(int action) {
@@ -1567,5 +1764,22 @@
                         + " required to call " + function);
             }
         }
+
+        private int getFocusedWindowId() {
+            // We call this only on window focus change or after touch
+            // exploration gesture end and the shown windows are not that
+            // many, so the linear look up is just fine.
+            IBinder token = mWindowManagerService.getFocusedWindowClientToken();
+            if (token != null) {
+                SparseArray<IBinder> windows = mWindowIdToWindowTokenMap;
+                final int windowCount = windows.size();
+                for (int i = 0; i < windowCount; i++) {
+                    if (windows.valueAt(i) == token) {
+                        return windows.keyAt(i);
+                    }
+                }
+            }
+            return -1;
+        }
     }
 }
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 39012e6..b0b2b8d 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -16,9 +16,6 @@
 
 package com.android.server.accessibility;
 
-import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END;
-import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START;
-
 import android.content.Context;
 import android.gesture.Gesture;
 import android.gesture.GestureLibraries;
@@ -26,17 +23,19 @@
 import android.gesture.GesturePoint;
 import android.gesture.GestureStroke;
 import android.gesture.Prediction;
+import android.graphics.Rect;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.util.Slog;
 import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicy;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 
-import com.android.server.input.InputFilter;
 import com.android.internal.R;
+import com.android.server.input.InputFilter;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -47,17 +46,18 @@
  * and consuming certain events. The interaction model is:
  *
  * <ol>
- *   <li>1. One finger moving around performs touch exploration.</li>
- *   <li>2. Two close fingers moving in the same direction perform a drag.</li>
- *   <li>3. Multi-finger gestures are delivered to view hierarchy.</li>
- *   <li>4. Pointers that have not moved more than a specified distance after they
+ *   <li>1. One finger moving slow around performs touch exploration.</li>
+ *   <li>2. One finger moving fast around performs gestures.</li>
+ *   <li>3. Two close fingers moving in the same direction perform a drag.</li>
+ *   <li>4. Multi-finger gestures are delivered to view hierarchy.</li>
+ *   <li>5. Pointers that have not moved more than a specified distance after they
  *          went down are considered inactive.</li>
- *   <li>5. Two fingers moving too far from each other or in different directions
- *          are considered a multi-finger gesture.</li>
- *   <li>6. Tapping on the last touch explored location within given time and
- *          distance slop performs a click.</li>
- *   <li>7. Tapping and holding for a while on the last touch explored location within
- *          given time and distance slop performs a long press.</li>
+ *   <li>6. Two fingers moving in different directions are considered a multi-finger gesture.</li>
+ *   <li>7. Double tapping clicks on the on the last touch explored location of it was in
+ *          a window that does not take focus, otherwise the click is within the accessibility
+ *          focused rectangle.</li>
+ *   <li>7. Tapping and holding for a while performs a long press in a similar fashion
+ *          as the click above.</li>
  * <ol>
  *
  * @hide
@@ -75,85 +75,116 @@
     private static final int STATE_DELEGATING = 0x00000004;
     private static final int STATE_GESTURE_DETECTING = 0x00000005;
 
-    // The time slop in milliseconds for activating an item after it has
-    // been touch explored. Tapping on an item within this slop will perform
-    // a click and tapping and holding down a long press.
-    private static final long ACTIVATION_TIME_SLOP = 2000;
-
     // The minimum of the cosine between the vectors of two moving
     // pointers so they can be considered moving in the same direction.
     private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
 
-    // The delay for sending a hover enter event.
-    private static final long DELAY_SEND_HOVER_ENTER = 200;
-
     // Constant referring to the ids bits of all pointers.
     private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
 
     // This constant captures the current implementation detail that
     // pointer IDs are between 0 and 31 inclusive (subject to change).
     // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
-    public static final int MAX_POINTER_COUNT = 32;
+    private static final int MAX_POINTER_COUNT = 32;
 
     // Invalid pointer ID.
-    public static final int INVALID_POINTER_ID = -1;
+    private static final int INVALID_POINTER_ID = -1;
+
+    // The velocity above which we detect gestures.
+    private static final int GESTURE_DETECTION_VELOCITY_DIP = 1000;
+
+    // The minimal distance before we take the middle of the distance between
+    // the two dragging pointers as opposed to use the location of the primary one.
+    private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200;
 
     // Temporary array for storing pointer IDs.
     private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT];
 
-    // The distance from the last touch explored location tapping within
-    // which would perform a click and tapping and holding a long press.
-    private final int mTouchExplorationTapSlop;
+    // Timeout within which we try to detect a tap.
+    private final int mTapTimeout;
+
+    // Timeout within which we try to detect a double tap.
+    private final int mDoubleTapTimeout;
+
+    // Slop between the down and up tap to be a tap.
+    private final int mTouchSlop;
+
+    // Slop between the first and second tap to be a double tap.
+    private final int mDoubleTapSlop;
 
     // The InputFilter this tracker is associated with i.e. the filter
     // which delegates event processing to this touch explorer.
     private final InputFilter mInputFilter;
 
-    // Handle to the accessibility manager for firing accessibility events
-    // announcing touch exploration gesture start and end.
-    private final AccessibilityManager mAccessibilityManager;
-
-    // The last event that was received while performing touch exploration.
-    private MotionEvent mLastTouchExploreEvent;
-
     // The current state of the touch explorer.
     private int mCurrentState = STATE_TOUCH_EXPLORING;
 
-    // Flag whether a touch exploration gesture is in progress.
-    private boolean mTouchExploreGestureInProgress;
-
     // The ID of the pointer used for dragging.
     private int mDraggingPointerId;
 
     // Handler for performing asynchronous operations.
     private final Handler mHandler;
 
-    // Command for delayed sending of a hover event.
-    private final SendHoverDelayed mSendHoverDelayed;
+    // Command for delayed sending of a hover enter event.
+    private final SendHoverDelayed mSendHoverEnterDelayed;
+
+    // Command for delayed sending of a hover exit event.
+    private final SendHoverDelayed mSendHoverExitDelayed;
 
     // Command for delayed sending of a long press.
     private final PerformLongPressDelayed mPerformLongPressDelayed;
 
+    // Helper to detect and react to double tap in touch explore mode.
+    private final DoubleTapDetector mDoubleTapDetector;
+
+    // The scaled minimal distance before we take the middle of the distance between
+    // the two dragging pointers as opposed to use the location of the primary one.
+    private final int mScaledMinPointerDistanceToUseMiddleLocation;
+
+    // The scaled velocity above which we detect gestures.
+    private final int mScaledGestureDetectionVelocity;
+
+    // Helper to track gesture velocity.
     private VelocityTracker mVelocityTracker;
 
+    // Helper class to track received pointers.
     private final ReceivedPointerTracker mReceivedPointerTracker;
 
+    // Helper class to track injected pointers.
     private final InjectedPointerTracker mInjectedPointerTracker;
 
-    private final GestureListener mGestureListener;
+    // Handle to the accessibility manager service.
+    private final AccessibilityManagerService mAms;
 
-    /**
-     * Callback for gesture detection.
-     */
-    public interface GestureListener {
+    // Temporary rectangle to avoid instantiation.
+    private final Rect mTempRect = new Rect();
 
-        /**
-         * Called when a given gesture was performed.
-         *
-         * @param gestureId The gesture id.
-         */
-        public boolean onGesture(int gestureId);
-    }
+    // The X of the previous event.
+    private float mPreviousX;
+
+    // The Y of the previous event.
+    private float mPreviousY;
+
+    // Buffer for storing points for gesture detection.
+    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
+
+    // The minimal delta between moves to add a gesture point.
+    private static final int TOUCH_TOLERANCE = 3;
+
+    // The minimal score for accepting a predicted gesture.
+    private static final float MIN_PREDICTION_SCORE = 2.0f;
+
+    // The library for gesture detection.
+    private GestureLibrary mGestureLibrary;
+
+    // The long pressing pointer id if coordinate remapping is needed.
+    private int mLongPressingPointerId;
+
+    // The long pressing pointer X if coordinate remapping is needed.
+    private int mLongPressingPointerDeltaX;
+
+    // The long pressing pointer Y if coordinate remapping is needed.
+    private int mLongPressingPointerDeltaY;
 
     /**
      * Creates a new instance.
@@ -162,25 +193,73 @@
      * @param context A context handle for accessing resources.
      */
     public TouchExplorer(InputFilter inputFilter, Context context,
-            GestureListener gestureListener) {
-        mGestureListener = gestureListener;
+            AccessibilityManagerService service) {
+        mAms = service;
         mReceivedPointerTracker = new ReceivedPointerTracker(context);
         mInjectedPointerTracker = new InjectedPointerTracker();
         mInputFilter = inputFilter;
-        mTouchExplorationTapSlop =
-            ViewConfiguration.get(context).getScaledTouchExploreTapSlop();
+        mTapTimeout = ViewConfiguration.getTapTimeout();
+        mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
         mHandler = new Handler(context.getMainLooper());
-        mSendHoverDelayed = new SendHoverDelayed();
         mPerformLongPressDelayed = new PerformLongPressDelayed();
-        mAccessibilityManager = AccessibilityManager.getInstance(context);
         mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
         mGestureLibrary.setOrientationStyle(4);
         mGestureLibrary.load();
+        mSendHoverEnterDelayed = new SendHoverDelayed(MotionEvent.ACTION_HOVER_ENTER, true);
+        mSendHoverExitDelayed = new SendHoverDelayed(MotionEvent.ACTION_HOVER_EXIT, false);
+        mDoubleTapDetector = new DoubleTapDetector();
+        final float density = context.getResources().getDisplayMetrics().density;
+        mScaledMinPointerDistanceToUseMiddleLocation =
+            (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density);
+        mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density);
+    }
+
+    public void clear() {
+        // If we have not received an event then we are in initial
+        // state. Therefore, there is not need to clean anything.
+        MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent();
+        if (event != null) {
+            clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED);
+        }
     }
 
     public void clear(MotionEvent event, int policyFlags) {
-        sendUpForInjectedDownPointers(event, policyFlags);
-        clear();
+        switch (mCurrentState) {
+            case STATE_TOUCH_EXPLORING: {
+                // If a touch exploration gesture is in progress send events for its end.
+                sendExitEventsIfNeeded(policyFlags);
+            } break;
+            case STATE_DRAGGING: {
+                mDraggingPointerId = INVALID_POINTER_ID;
+                // Send exit to all pointers that we have delivered.
+                sendUpForInjectedDownPointers(event, policyFlags);
+            } break;
+            case STATE_DELEGATING: {
+                // Send exit to all pointers that we have delivered.
+                sendUpForInjectedDownPointers(event, policyFlags);
+            } break;
+            case STATE_GESTURE_DETECTING: {
+                // Clear the current stroke.
+                mStrokeBuffer.clear();
+            } break;
+        }
+        // Remove all pending callbacks.
+        mSendHoverEnterDelayed.remove();
+        mSendHoverExitDelayed.remove();
+        mPerformLongPressDelayed.remove();
+        // Reset the pointer trackers.
+        mReceivedPointerTracker.clear();
+        mInjectedPointerTracker.clear();
+        // Clear the double tap detector
+        mDoubleTapDetector.clear();
+        // Go to initial state.
+        // Clear the long pressing pointer remap data.
+        mLongPressingPointerId = -1;
+        mLongPressingPointerDeltaX = 0;
+        mLongPressingPointerDeltaY = 0;
+        mCurrentState = STATE_TOUCH_EXPLORING;
     }
 
     public void onMotionEvent(MotionEvent event, int policyFlags) {
@@ -218,7 +297,6 @@
      */
     private void handleMotionEventStateTouchExploring(MotionEvent event, int policyFlags) {
         ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
-        InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
         final int activePointerCount = receivedTracker.getActivePointerCount();
 
         if (mVelocityTracker == null) {
@@ -226,8 +304,16 @@
         }
         mVelocityTracker.addMovement(event);
 
+        mDoubleTapDetector.onMotionEvent(event, policyFlags);
+
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
+                // Pre-feed the motion events to the gesture detector since we
+                // have a distance slop before getting into gesture detection
+                // mode and not using the points within this slop significantly
+                // decreases the quality of gesture recognition.
+                handleMotionEventGestureDetecting(event, policyFlags);
+                //$FALL-THROUGH$
             case MotionEvent.ACTION_POINTER_DOWN: {
                 switch (activePointerCount) {
                     case 0: {
@@ -235,44 +321,31 @@
                                 + "touch exploring state!");
                     }
                     case 1: {
-                        mSendHoverDelayed.remove();
-                        mPerformLongPressDelayed.remove();
-                        // Send a hover for every finger down so the user gets feedback.
+                        // If we still have not notified the user for the last
+                        // touch, we figure out what to do. If were waiting
+                        // we resent the delayed callback and wait again.
+                        if (mSendHoverEnterDelayed.isPending()) {
+                            mSendHoverEnterDelayed.remove();
+                            mSendHoverExitDelayed.remove();
+                            mPerformLongPressDelayed.remove();
+                        }
+
+                        // If we have the first tap schedule a long press and break
+                        // since we do not want to schedule hover enter because
+                        // the delayed callback will kick in before the long click.
+                        // This would lead to a state transition resulting in long
+                        // pressing the item below the double taped area which is
+                        // not necessary where accessibility focus is.
+                        if (mDoubleTapDetector.firstTapDetected()) {
+                            // We got a tap now post a long press action.
+                            mPerformLongPressDelayed.post(event, policyFlags);
+                            break;
+                        }
+                        // Deliver hover enter with a delay to have a chance
+                        // to detect what the user is trying to do.
                         final int pointerId = receivedTracker.getPrimaryActivePointerId();
                         final int pointerIdBits = (1 << pointerId);
-                        final int lastAction = injectedTracker.getLastInjectedHoverAction();
-
-                        // Deliver hover enter with a delay to have a change to detect
-                        // whether the user actually starts a scrolling gesture.
-                        if (lastAction == MotionEvent.ACTION_HOVER_EXIT) {
-                            mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER,
-                                    pointerIdBits, policyFlags, DELAY_SEND_HOVER_ENTER);
-                        } else {
-                            sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
-                                    policyFlags);
-                        }
-
-                        if (mLastTouchExploreEvent == null) {
-                            break;
-                        }
-
-                        // If more pointers down on the screen since the last touch
-                        // exploration we discard the last cached touch explore event.
-                        if (event.getPointerCount() != mLastTouchExploreEvent.getPointerCount()) {
-                            mLastTouchExploreEvent = null;
-                            break;
-                        }
-
-                        // If the down is in the time slop => schedule a long press.
-                        final long pointerDownTime =
-                            receivedTracker.getReceivedPointerDownTime(pointerId);
-                        final long lastExploreTime = mLastTouchExploreEvent.getEventTime();
-                        final long deltaTimeExplore = pointerDownTime - lastExploreTime;
-                        if (deltaTimeExplore <= ACTIVATION_TIME_SLOP) {
-                            mPerformLongPressDelayed.post(event, policyFlags,
-                                    ViewConfiguration.getLongPressTimeout());
-                            break;
-                        }
+                        mSendHoverEnterDelayed.post(event, pointerIdBits, policyFlags);
                     } break;
                     default: {
                         /* do nothing - let the code for ACTION_MOVE decide what to do */
@@ -288,119 +361,130 @@
                         /* do nothing - no active pointers so we swallow the event */
                     } break;
                     case 1: {
-                        // Detect touch exploration gesture start by having one active pointer
-                        // that moved more than a given distance.
-                        if (!mTouchExploreGestureInProgress) {
+                        // We have not started sending events since we try to
+                        // figure out what the user is doing.
+                        if (mSendHoverEnterDelayed.isPending()) {
+                            // Pre-feed the motion events to the gesture detector since we
+                            // have a distance slop before getting into gesture detection
+                            // mode and not using the points within this slop significantly
+                            // decreases the quality of gesture recognition.
+                            handleMotionEventGestureDetecting(event, policyFlags);
+
                             final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId)
                                 - event.getX(pointerIndex);
                             final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId)
                                 - event.getY(pointerIndex);
                             final double moveDelta = Math.hypot(deltaX, deltaY);
-
-                            if (moveDelta > mTouchExplorationTapSlop) {
-
+                            // The user has moved enough for us to decide.
+                            if (moveDelta > mDoubleTapSlop) {
+                                // Check whether the user is performing a gesture. We
+                                // detect gestures if the pointer is moving above a
+                                // given velocity.
                                 mVelocityTracker.computeCurrentVelocity(1000);
                                 final float maxAbsVelocity = Math.max(
                                         Math.abs(mVelocityTracker.getXVelocity(pointerId)),
                                         Math.abs(mVelocityTracker.getYVelocity(pointerId)));
-                                // TODO: Tune the velocity cut off and add a constant.
-                                if (maxAbsVelocity > 1000) {
-                                    clear(event, policyFlags);
+                                if (maxAbsVelocity > mScaledGestureDetectionVelocity) {
+                                    // We have to perform gesture detection, so
+                                    // clear the current state and try to detect.
                                     mCurrentState = STATE_GESTURE_DETECTING;
-                                    event.setAction(MotionEvent.ACTION_DOWN);
-                                    handleMotionEventGestureDetecting(event, policyFlags);
-                                    return;
-                                }
-
-                                mTouchExploreGestureInProgress = true;
-                                sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
-                                // Make sure the scheduled down/move event is sent.
-                                mSendHoverDelayed.forceSendAndRemove();
-                                mPerformLongPressDelayed.remove();
-                                // If we have transitioned to exploring state from another one
-                                // we need to send a hover enter event here.
-                                final int lastAction = injectedTracker.getLastInjectedHoverAction();
-                                if (lastAction == MotionEvent.ACTION_HOVER_EXIT) {
-                                    sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER,
+                                    mSendHoverEnterDelayed.remove();
+                                    mSendHoverExitDelayed.remove();
+                                    mPerformLongPressDelayed.remove();
+                                } else {
+                                    // We have just decided that the user is touch,
+                                    // exploring so start sending events.
+                                    mSendHoverEnterDelayed.forceSendAndRemove();
+                                    mSendHoverExitDelayed.remove();
+                                    sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE,
                                             pointerIdBits, policyFlags);
                                 }
-                                sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
-                                        policyFlags);
+                                break;
                             }
                         } else {
-                            // Touch exploration gesture in progress so send a hover event.
+                            // The user is wither double tapping or performing long
+                            // press so do not send move events yet.
+                            if (mDoubleTapDetector.firstTapDetected()) {
+                                break;
+                            }
+                            sendEnterEventsIfNeeded(policyFlags);
                             sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
                                     policyFlags);
                         }
-
-                        // If the exploring pointer moved enough => cancel the long press.
-                        if (!mTouchExploreGestureInProgress && mLastTouchExploreEvent != null
-                                && mPerformLongPressDelayed.isPenidng()) {
-
-                            // If the pointer moved more than the tap slop => cancel long press.
-                            final float deltaX = mLastTouchExploreEvent.getX(pointerIndex)
-                                    - event.getX(pointerIndex);
-                            final float deltaY = mLastTouchExploreEvent.getY(pointerIndex)
-                                    - event.getY(pointerIndex);
-                            final float moveDelta = (float) Math.hypot(deltaX, deltaY);
-                            if (moveDelta > mTouchExplorationTapSlop) {
-                                mLastTouchExploreEvent = null;
-                                mPerformLongPressDelayed.remove();
-                                break;
-                            }
-                        }
                     } break;
                     case 2: {
-                        mSendHoverDelayed.remove();
-                        mPerformLongPressDelayed.remove();
-                        // We want to no longer hover over the location so subsequent
-                        // touch at the same spot will generate a hover enter.
-                        ensureHoverExitSent(event, pointerIdBits, policyFlags);
+                        // More than one pointer so the user is not touch exploring
+                        // and now we have to decide whether to delegate or drag.
+                        if (mSendHoverEnterDelayed.isPending()) {
+                            // We have not started sending events so cancel
+                            // scheduled sending events.
+                            mSendHoverEnterDelayed.remove();
+                            mSendHoverExitDelayed.remove();
+                            mPerformLongPressDelayed.remove();
+                        } else {
+                            // If the user is touch exploring the second pointer may be
+                            // performing a double tap to activate an item without need
+                            // for the user to lift his exploring finger.
+                            final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId)
+                                    - event.getX(pointerIndex);
+                            final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId)
+                                    - event.getY(pointerIndex);
+                            final double moveDelta = Math.hypot(deltaX, deltaY);
+                            if (moveDelta < mDoubleTapSlop) {
+                                break;
+                            }
+                            // We are sending events so send exit and gesture
+                            // end since we transition to another state.
+                            sendExitEventsIfNeeded(policyFlags);
+                        }
+
+                        // We know that a new state transition is to happen and the
+                        // new state will not be gesture recognition, so clear the
+                        // stashed gesture strokes.
+                        mStrokeBuffer.clear();
 
                         if (isDraggingGesture(event)) {
                             // Two pointers moving in the same direction within
                             // a given distance perform a drag.
+                            mSendHoverEnterDelayed.remove();
+                            mSendHoverExitDelayed.remove();
+                            mPerformLongPressDelayed.remove();
                             mCurrentState = STATE_DRAGGING;
-                            if (mTouchExploreGestureInProgress) {
-                                sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
-                                mTouchExploreGestureInProgress = false;
-                            }
-                            mLastTouchExploreEvent = null;
                             mDraggingPointerId = pointerId;
                             sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits,
                                     policyFlags);
                         } else {
                             // Two pointers moving arbitrary are delegated to the view hierarchy.
                             mCurrentState = STATE_DELEGATING;
-                            mSendHoverDelayed.remove();
-                            if (mTouchExploreGestureInProgress) {
-                                sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
-                                mTouchExploreGestureInProgress = false;
-                            }
-                            mLastTouchExploreEvent = null;
                             sendDownForAllActiveNotInjectedPointers(event, policyFlags);
                         }
                     } break;
                     default: {
-                        mSendHoverDelayed.remove();
-                        mPerformLongPressDelayed.remove();
-                        // We want to no longer hover over the location so subsequent
-                        // touch at the same spot will generate a hover enter.
-                        ensureHoverExitSent(event, pointerIdBits, policyFlags);
+                        // More than one pointer so the user is not touch exploring
+                        // and now we have to decide whether to delegate or drag.
+                        if (mSendHoverEnterDelayed.isPending()) {
+                            // We have not started sending events so cancel
+                            // scheduled sending events.
+                            mSendHoverEnterDelayed.remove();
+                            mSendHoverExitDelayed.remove();
+                            mPerformLongPressDelayed.remove();
+                        } else {
+                            // We are sending events so send exit and gesture
+                            // end since we transition to another state.
+                            sendExitEventsIfNeeded(policyFlags);
+                        }
 
                         // More than two pointers are delegated to the view hierarchy.
                         mCurrentState = STATE_DELEGATING;
-                        mSendHoverDelayed.remove();
-                        if (mTouchExploreGestureInProgress) {
-                            sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
-                            mTouchExploreGestureInProgress = false;
-                        }
-                        mLastTouchExploreEvent = null;
                         sendDownForAllActiveNotInjectedPointers(event, policyFlags);
                     }
                 }
             } break;
             case MotionEvent.ACTION_UP:
+                // We know that we do not need the pre-fed gesture points are not
+                // needed anymore since the last pointer just went up.
+                mStrokeBuffer.clear();
+                //$FALL-THROUGH$
             case MotionEvent.ACTION_POINTER_UP: {
                 final int pointerId = receivedTracker.getLastReceivedUpPointerId();
                 final int pointerIdBits = (1 << pointerId);
@@ -413,59 +497,12 @@
 
                         mPerformLongPressDelayed.remove();
 
-                        // If touch exploring announce the end of the gesture.
-                        // Also do not click on the last explored location.
-                        if (mTouchExploreGestureInProgress) {
-                            mTouchExploreGestureInProgress = false;
-                            mSendHoverDelayed.forceSendAndRemove();
-                            ensureHoverExitSent(event, pointerIdBits, policyFlags);
-                            mLastTouchExploreEvent = MotionEvent.obtain(event);
-                            sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
-                            break;
-                        }
-
-                        // Detect whether to activate i.e. click on the last explored location.
-                        if (mLastTouchExploreEvent != null) {
-                            // If the down was not in the time slop => nothing else to do.
-                            final long eventTime =
-                                receivedTracker.getLastReceivedUpPointerDownTime();
-                            final long exploreTime = mLastTouchExploreEvent.getEventTime();
-                            final long deltaTime = eventTime - exploreTime;
-                            if (deltaTime > ACTIVATION_TIME_SLOP) {
-                                mSendHoverDelayed.forceSendAndRemove();
-                                ensureHoverExitSent(event, pointerIdBits, policyFlags);
-                                mLastTouchExploreEvent = MotionEvent.obtain(event);
-                                break;
-                            }
-
-                            // If a tap is farther than the tap slop => nothing to do.
-                            final int pointerIndex = event.findPointerIndex(pointerId);
-                            final float deltaX = mLastTouchExploreEvent.getX(pointerIndex)
-                                    - event.getX(pointerIndex);
-                            final float deltaY = mLastTouchExploreEvent.getY(pointerIndex)
-                                    - event.getY(pointerIndex);
-                            final float deltaMove = (float) Math.hypot(deltaX, deltaY);
-                            if (deltaMove > mTouchExplorationTapSlop) {
-                                mSendHoverDelayed.forceSendAndRemove();
-                                ensureHoverExitSent(event, pointerIdBits, policyFlags);
-                                mLastTouchExploreEvent = MotionEvent.obtain(event);
-                                break;
-                            }
-
-                            // This is a tap so do not send hover events since
-                            // this events will result in firing the corresponding
-                            // accessibility events confusing the user about what
-                            // is actually clicked.
-                            mSendHoverDelayed.remove();
-                            ensureHoverExitSent(event, pointerIdBits, policyFlags);
-
-                            // All preconditions are met, so click the last explored location.
-                            sendActionDownAndUp(mLastTouchExploreEvent, policyFlags);
-                            mLastTouchExploreEvent = null;
+                        // If we have not delivered the enter schedule exit.
+                        if (mSendHoverEnterDelayed.isPending()) {
+                            mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags);
                         } else {
-                            mSendHoverDelayed.forceSendAndRemove();
-                            ensureHoverExitSent(event, pointerIdBits, policyFlags);
-                            mLastTouchExploreEvent = MotionEvent.obtain(event);
+                            // The user is touch exploring so we send events for end.
+                            sendExitEventsIfNeeded(policyFlags);
                         }
                     } break;
                 }
@@ -475,16 +512,7 @@
                 }
             } break;
             case MotionEvent.ACTION_CANCEL: {
-                mSendHoverDelayed.remove();
-                mPerformLongPressDelayed.remove();
-                final int pointerId = receivedTracker.getPrimaryActivePointerId();
-                final int pointerIdBits = (1 << pointerId);                
-                ensureHoverExitSent(event, pointerIdBits, policyFlags);
-                clear();
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.clear();
-                    mVelocityTracker = null;
-                }
+                clear(event, policyFlags);
             } break;
         }
     }
@@ -517,6 +545,28 @@
                     } break;
                     case 2: {
                         if (isDraggingGesture(event)) {
+                            // If the dragging pointer are closer that a given distance we
+                            // use the location of the primary one. Otherwise, we take the
+                            // middle between the pointers.
+                            int[] pointerIds = mTempPointerIds;
+                            mReceivedPointerTracker.populateActivePointerIds(pointerIds);
+
+                            final int firstPtrIndex = event.findPointerIndex(pointerIds[0]);
+                            final int secondPtrIndex = event.findPointerIndex(pointerIds[1]);
+
+                            final float firstPtrX = event.getX(firstPtrIndex);
+                            final float firstPtrY = event.getY(firstPtrIndex);
+                            final float secondPtrX = event.getX(secondPtrIndex);
+                            final float secondPtrY = event.getY(secondPtrIndex);
+
+                            final float deltaX = firstPtrX - secondPtrX;
+                            final float deltaY = firstPtrY - secondPtrY;
+                            final double distance = Math.hypot(deltaX, deltaY);
+
+                            if (distance > mScaledMinPointerDistanceToUseMiddleLocation) {
+                                event.setLocation(deltaX / 2, deltaY / 2);
+                            }
+
                             // If still dragging send a drag event.
                             sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits,
                                     policyFlags);
@@ -557,7 +607,7 @@
                 mCurrentState = STATE_TOUCH_EXPLORING;
             } break;
             case MotionEvent.ACTION_CANCEL: {
-                clear();
+                clear(event, policyFlags);
             } break;
         }
     }
@@ -574,9 +624,6 @@
                 throw new IllegalStateException("Delegating state can only be reached if "
                         + "there is at least one pointer down!");
             }
-            case MotionEvent.ACTION_UP: {
-                mCurrentState = STATE_TOUCH_EXPLORING;
-            } break;
             case MotionEvent.ACTION_MOVE: {
                 // Check  whether some other pointer became active because they have moved
                 // a given distance and if such exist send them to the view hierarchy
@@ -587,30 +634,24 @@
                     sendDownForAllActiveNotInjectedPointers(prototype, policyFlags);
                 }
             } break;
+            case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_POINTER_UP: {
+                mLongPressingPointerId = -1;
+                mLongPressingPointerDeltaX = 0;
+                mLongPressingPointerDeltaY = 0;
                 // No active pointers => go to initial state.
                 if (mReceivedPointerTracker.getActivePointerCount() == 0) {
                     mCurrentState = STATE_TOUCH_EXPLORING;
                 }
             } break;
             case MotionEvent.ACTION_CANCEL: {
-                clear();
+                clear(event, policyFlags);
             } break;
         }
         // Deliver the event striping out inactive pointers.
         sendMotionEventStripInactivePointers(event, policyFlags);
     }
 
-    private float mPreviousX;
-    private float mPreviousY;
-
-    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
-
-    private static final int TOUCH_TOLERANCE = 3;
-    private static final float MIN_PREDICTION_SCORE = 2.0f;
-
-    private GestureLibrary mGestureLibrary;
-
     private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) {
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
@@ -631,8 +672,7 @@
                     mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
                 }
             } break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_POINTER_UP: {
+            case MotionEvent.ACTION_UP: {
                 float x = event.getX();
                 float y = event.getY();
                 mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
@@ -650,7 +690,7 @@
                         }
                         try {
                             final int gestureId = Integer.parseInt(bestPrediction.name);
-                            mGestureListener.onGesture(gestureId);
+                            mAms.onGesture(gestureId);
                         } catch (NumberFormatException nfe) {
                             Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name);
                         }
@@ -661,8 +701,7 @@
                 mCurrentState = STATE_TOUCH_EXPLORING;
             } break;
             case MotionEvent.ACTION_CANCEL: {
-                mStrokeBuffer.clear();
-                mCurrentState = STATE_TOUCH_EXPLORING;
+                clear(event, policyFlags);
             } break;
         }
     }
@@ -706,17 +745,32 @@
     }
 
     /**
-     * Ensures that hover exit has been sent.
+     * Sends the exit events if needed. Such events are hover exit and touch explore
+     * gesture end.
      *
-     * @param prototype The prototype from which to create the injected events.
-     * @param pointerIdBits The bits of the pointers to send.
      * @param policyFlags The policy flags associated with the event.
      */
-    private void ensureHoverExitSent(MotionEvent prototype, int pointerIdBits, int policyFlags) {
-        final int lastAction = mInjectedPointerTracker.getLastInjectedHoverAction();
-        if (lastAction != MotionEvent.ACTION_HOVER_EXIT) {
-            sendMotionEvent(prototype, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits,
-                    policyFlags);
+    private void sendExitEventsIfNeeded(int policyFlags) {
+        MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
+        if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
+            final int pointerIdBits = event.getPointerIdBits();
+            mAms.touchExplorationGestureEnded();
+            sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
+        }
+    }
+
+    /**
+     * Sends the enter events if needed. Such events are hover enter and touch explore
+     * gesture start.
+     *
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void sendEnterEventsIfNeeded(int policyFlags) {
+        MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
+        if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
+            final int pointerIdBits = event.getPointerIdBits();
+            mAms.touchExplorationGestureStarted();
+            sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
         }
     }
 
@@ -826,6 +880,36 @@
             event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime());
         }
 
+        // If the user is long pressing but the long pressing pointer
+        // was not exactly over the accessibility focused item we need
+        // to remap the location of that pointer so the user does not
+        // have to explicitly touch explore something to be able to
+        // long press it, or even worse to avoid the user long pressing
+        // on the wrong item since click and long press behave differently.
+        if (mLongPressingPointerId >= 0) {
+            final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
+            final int pointerCount = event.getPointerCount();
+            PointerProperties[] props = PointerProperties.createArray(pointerCount);
+            PointerCoords[] coords = PointerCoords.createArray(pointerCount);
+            for (int i = 0; i < pointerCount; i++) {
+                event.getPointerProperties(i, props[i]);
+                event.getPointerCoords(i, coords[i]);
+                if (i == remappedIndex) {
+                    coords[i].x -= mLongPressingPointerDeltaX;
+                    coords[i].y -= mLongPressingPointerDeltaY;
+                }
+            }
+            MotionEvent remapped = MotionEvent.obtain(event.getDownTime(),
+                    event.getEventTime(), event.getAction(), event.getPointerCount(),
+                    props, coords, event.getMetaState(), event.getButtonState(),
+                    1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
+                    event.getSource(), event.getFlags());
+            if (event != prototype) {
+                event.recycle();
+            }
+            event = remapped;
+        }
+
         if (DEBUG) {
             Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x"
                     + Integer.toHexString(policyFlags));
@@ -878,6 +962,172 @@
         }
     }
 
+    private class DoubleTapDetector {
+        private MotionEvent mDownEvent;
+        private MotionEvent mFirstTapEvent;
+
+        public void onMotionEvent(MotionEvent event, int policyFlags) {
+            final int action = event.getActionMasked();
+            switch (action) {
+                case MotionEvent.ACTION_DOWN:
+                case MotionEvent.ACTION_POINTER_DOWN: {
+                    if (mFirstTapEvent != null && !isSamePointerContext(mFirstTapEvent, event)) {
+                        clear();
+                    }
+                    mDownEvent = MotionEvent.obtain(event);
+                } break;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_POINTER_UP: {
+                    if (mDownEvent == null) {
+                        return;
+                    }
+                    if (!isSamePointerContext(mDownEvent, event)) {
+                        clear();
+                        return;
+                    }
+                    if (isTap(mDownEvent, event)) {
+                        if (mFirstTapEvent == null || isTimedOut(mFirstTapEvent, event,
+                                mDoubleTapTimeout)) {
+                            mFirstTapEvent = MotionEvent.obtain(event);
+                            mDownEvent.recycle();
+                            mDownEvent = null;
+                            return;
+                        }
+                        if (isDoubleTap(mFirstTapEvent, event)) {
+                            onDoubleTap(event, policyFlags);
+                            mFirstTapEvent.recycle();
+                            mFirstTapEvent = null;
+                            mDownEvent.recycle();
+                            mDownEvent = null;
+                            return;
+                        }
+                        mFirstTapEvent.recycle();
+                        mFirstTapEvent = null;
+                    } else {
+                        if (mFirstTapEvent != null) {
+                            mFirstTapEvent.recycle();
+                            mFirstTapEvent = null;
+                        }
+                    }
+                    mDownEvent.recycle();
+                    mDownEvent = null;
+                } break;
+            }
+        }
+
+        public void onDoubleTap(MotionEvent secondTapUp, int policyFlags) {
+            // This should never be called when more than two pointers are down.
+            if (secondTapUp.getPointerCount() > 2) {
+                return;
+            }
+
+            // Remove pending event deliveries.
+            mSendHoverEnterDelayed.remove();
+            mSendHoverExitDelayed.remove();
+            mPerformLongPressDelayed.remove();
+
+            // This is a tap so do not send hover events since
+            // this events will result in firing the corresponding
+            // accessibility events confusing the user about what
+            // is actually clicked.
+            sendExitEventsIfNeeded(policyFlags);
+
+            // If the last touched explored location is not within the focused
+            // window we will click at that exact spot, otherwise we find the
+            // accessibility focus and if the tap is within its bounds we click
+            // there, otherwise we pick the middle of the focus rectangle.
+            MotionEvent lastEvent = mInjectedPointerTracker.getLastInjectedHoverEvent();
+            if (lastEvent == null) {
+                return;
+            }
+
+            final int exploreLocationX = (int) lastEvent.getX(lastEvent.getActionIndex());
+            final int exploreLocationY = (int) lastEvent.getY(lastEvent.getActionIndex());
+
+            Rect bounds = mTempRect;
+            boolean useLastHoverLocation = false;
+
+            final int pointerId = secondTapUp.getPointerId(secondTapUp.getActionIndex());
+            final int pointerIndex = secondTapUp.findPointerIndex(pointerId);
+            if (mAms.getAccessibilityFocusBounds(exploreLocationX, exploreLocationY, bounds)) {
+                // If the user's last touch explored location is not
+                // within the accessibility focus bounds we use the center
+                // of the accessibility focused rectangle.
+                if (!bounds.contains((int) secondTapUp.getX(pointerIndex),
+                        (int) secondTapUp.getY(pointerIndex))) {
+                    useLastHoverLocation = true;
+                }
+            }
+
+            // Do the click.
+            PointerProperties[] properties = new PointerProperties[1];
+            properties[0] = new PointerProperties();
+            secondTapUp.getPointerProperties(pointerIndex, properties[0]);
+            PointerCoords[] coords = new PointerCoords[1];
+            coords[0] = new PointerCoords();
+            coords[0].x = (useLastHoverLocation) ? bounds.centerX() : exploreLocationX;
+            coords[0].y = (useLastHoverLocation) ? bounds.centerY() : exploreLocationY;
+            MotionEvent event = MotionEvent.obtain(secondTapUp.getDownTime(),
+                    secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
+                    coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0,
+                    secondTapUp.getSource(), secondTapUp.getFlags());
+            sendActionDownAndUp(event, policyFlags);
+            event.recycle();
+        }
+
+        public void clear() {
+            if (mDownEvent != null) {
+                mDownEvent.recycle();
+                mDownEvent = null;
+            }
+            if (mFirstTapEvent != null) {
+                mFirstTapEvent.recycle();
+                mFirstTapEvent = null;
+            }
+        }
+
+        public boolean isTap(MotionEvent down, MotionEvent up) {
+            return eventsWithinTimeoutAndDistance(down, up, mTapTimeout, mTouchSlop);
+        }
+
+        private boolean isDoubleTap(MotionEvent firstUp, MotionEvent secondUp) {
+            return eventsWithinTimeoutAndDistance(firstUp, secondUp, mDoubleTapTimeout,
+                    mDoubleTapSlop);
+        }
+
+        private boolean eventsWithinTimeoutAndDistance(MotionEvent first, MotionEvent second,
+                int timeout, int distance) {
+            if (isTimedOut(first, second, timeout)) {
+                return false;
+            }
+            final int downPtrIndex = first.getActionIndex();
+            final int upPtrIndex = second.getActionIndex();
+            final float deltaX = second.getX(upPtrIndex) - first.getX(downPtrIndex);
+            final float deltaY = second.getY(upPtrIndex) - first.getY(downPtrIndex);
+            final double deltaMove = Math.hypot(deltaX, deltaY);
+            if (deltaMove >= distance) {
+                return false;
+            }
+            return true;
+        }
+
+        private boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) {
+            final long deltaTime = secondUp.getEventTime() - firstUp.getEventTime();
+            return (deltaTime >= timeout);
+        }
+
+        private boolean isSamePointerContext(MotionEvent first, MotionEvent second) {
+            return (first.getPointerIdBits() == second.getPointerIdBits()
+                    && first.getPointerId(first.getActionIndex())
+                            == second.getPointerId(second.getActionIndex()));
+        }
+
+        public boolean firstTapDetected() {
+            return mFirstTapEvent != null
+                && SystemClock.uptimeMillis() - mFirstTapEvent.getEventTime() < mDoubleTapTimeout;
+        }
+    }
+
     /**
      * Determines whether a two pointer gesture is a dragging one.
      *
@@ -940,30 +1190,6 @@
         return true;
     }
 
-   /**
-    * Sends an event announcing the start/end of a touch exploration gesture.
-    *
-    * @param eventType The type of the event to send.
-    */
-    private void sendAccessibilityEvent(int eventType) {
-        AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        mAccessibilityManager.sendAccessibilityEvent(event);
-    }
-
-    /**
-     * Clears the internal state of this explorer.
-     */
-    public void clear() {
-        mSendHoverDelayed.remove();
-        mPerformLongPressDelayed.remove();
-        mReceivedPointerTracker.clear();
-        mInjectedPointerTracker.clear();
-        mLastTouchExploreEvent = null;
-        mCurrentState = STATE_TOUCH_EXPLORING;
-        mTouchExploreGestureInProgress = false;
-        mDraggingPointerId = INVALID_POINTER_ID;
-    }
-
     /**
      * Gets the symbolic name of a state.
      *
@@ -1002,10 +1228,10 @@
         private MotionEvent mEvent;
         private int mPolicyFlags;
 
-        public void post(MotionEvent prototype, int policyFlags, long delay) {
+        public void post(MotionEvent prototype, int policyFlags) {
             mEvent = MotionEvent.obtain(prototype);
             mPolicyFlags = policyFlags;
-            mHandler.postDelayed(this, delay);
+            mHandler.postDelayed(this, ViewConfiguration.getLongPressTimeout());
         }
 
         public void remove() {
@@ -1021,16 +1247,29 @@
 
         @Override
         public void run() {
-            mCurrentState = STATE_DELEGATING;
-            // Make sure the scheduled hover exit is delivered.
-            mSendHoverDelayed.remove();
+            final int pointerIndex = mEvent.getActionIndex();
+            final int eventX = (int) mEvent.getX(pointerIndex);
+            final int eventY = (int) mEvent.getY(pointerIndex);
+            Rect bounds = mTempRect;
+            if (mAms.getAccessibilityFocusBounds(eventX, eventY, bounds)
+                    && !bounds.contains(eventX, eventY)) {
+                mLongPressingPointerId = mEvent.getPointerId(pointerIndex);
+                mLongPressingPointerDeltaX = eventX - bounds.centerX();
+                mLongPressingPointerDeltaY = eventY - bounds.centerY();
+            } else {
+                mLongPressingPointerId = -1;
+                mLongPressingPointerDeltaX = 0;
+                mLongPressingPointerDeltaY = 0;
+            }
+            // We are sending events so send exit and gesture
+            // end since we transition to another state.
             final int pointerId = mReceivedPointerTracker.getPrimaryActivePointerId();
             final int pointerIdBits = (1 << pointerId);
-            ensureHoverExitSent(mEvent, pointerIdBits, mPolicyFlags);
+            mAms.touchExplorationGestureEnded();
+            sendExitEventsIfNeeded(mPolicyFlags);
 
+            mCurrentState = STATE_DELEGATING;
             sendDownForAllActiveNotInjectedPointers(mEvent, mPolicyFlags);
-            mTouchExploreGestureInProgress = false;
-            mLastTouchExploreEvent = null;
             clear();
         }
 
@@ -1047,20 +1286,41 @@
     /**
      * Class for delayed sending of hover events.
      */
-    private final class SendHoverDelayed implements Runnable {
-        private MotionEvent mEvent;
-        private int mAction;
+    class SendHoverDelayed implements Runnable {
+        private final String LOG_TAG_SEND_HOVER_DELAYED = SendHoverDelayed.class.getName();
+
+        private final int mHoverAction;
+        private final boolean mGestureStarted;
+
+        private MotionEvent mPrototype;
         private int mPointerIdBits;
         private int mPolicyFlags;
 
-        public void post(MotionEvent prototype, int action, int pointerIdBits, int policyFlags,
-                long delay) {
+        public SendHoverDelayed(int hoverAction, boolean gestureStarted) {
+            mHoverAction = hoverAction;
+            mGestureStarted = gestureStarted;
+        }
+
+        public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) {
             remove();
-            mEvent = MotionEvent.obtain(prototype);
-            mAction = action;
+            mPrototype = MotionEvent.obtain(prototype);
             mPointerIdBits = pointerIdBits;
             mPolicyFlags = policyFlags;
-            mHandler.postDelayed(this, delay);
+            mHandler.postDelayed(this, mTapTimeout);
+        }
+
+        public float getX() {
+            if (isPending()) {
+                return mPrototype.getX();
+            }
+            return 0;
+        }
+
+        public float getY() {
+            if (isPending()) {
+                return mPrototype.getY();
+            }
+            return 0;
         }
 
         public void remove() {
@@ -1068,23 +1328,22 @@
             clear();
         }
 
-        private boolean isPenidng() {
-            return (mEvent != null);
+        private boolean isPending() {
+            return (mPrototype != null);
         }
 
         private void clear() {
-            if (!isPenidng()) {
+            if (!isPending()) {
                 return;
             }
-            mEvent.recycle();
-            mEvent = null;
-            mAction = 0;
+            mPrototype.recycle();
+            mPrototype = null;
             mPointerIdBits = -1;
             mPolicyFlags = 0;
         }
 
         public void forceSendAndRemove() {
-            if (isPenidng()) {
+            if (isPending()) {
                 run();
                 remove();
             }
@@ -1092,16 +1351,17 @@
 
         public void run() {
             if (DEBUG) {
-                if (mAction == MotionEvent.ACTION_HOVER_ENTER) {
-                    Slog.d(LOG_TAG, "Injecting: " + MotionEvent.ACTION_HOVER_ENTER);
-                } else if (mAction == MotionEvent.ACTION_HOVER_MOVE) {
-                    Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_MOVE");
-                } else if (mAction == MotionEvent.ACTION_HOVER_EXIT) {
-                    Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_EXIT");
-                }
+                Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event: "
+                        + MotionEvent.actionToString(mHoverAction));
+                Slog.d(LOG_TAG_SEND_HOVER_DELAYED, mGestureStarted ?
+                        "touchExplorationGestureStarted" : "touchExplorationGestureEnded");
             }
-
-            sendMotionEvent(mEvent, mAction, mPointerIdBits, mPolicyFlags);
+            if (mGestureStarted) {
+                mAms.touchExplorationGestureStarted();
+            } else {
+                mAms.touchExplorationGestureEnded();
+            }
+            sendMotionEvent(mPrototype, mHoverAction, mPointerIdBits, mPolicyFlags);
             clear();
         }
     }
@@ -1120,8 +1380,8 @@
         // The time of the last injected down.
         private long mLastInjectedDownEventTime;
 
-        // The action of the last injected hover event.
-        private int mLastInjectedHoverEventAction = MotionEvent.ACTION_HOVER_EXIT;
+        // The last injected hover event.
+        private MotionEvent mLastInjectedHoverEvent;
 
         /**
          * Processes an injected {@link MotionEvent} event.
@@ -1150,11 +1410,14 @@
                 case MotionEvent.ACTION_HOVER_ENTER:
                 case MotionEvent.ACTION_HOVER_MOVE:
                 case MotionEvent.ACTION_HOVER_EXIT: {
-                    mLastInjectedHoverEventAction = event.getActionMasked();
+                    if (mLastInjectedHoverEvent != null) {
+                        mLastInjectedHoverEvent.recycle();
+                    }
+                    mLastInjectedHoverEvent = MotionEvent.obtain(event);
                 } break;
             }
             if (DEBUG) {
-                Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer: " + toString());
+                Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString());
             }
         }
 
@@ -1198,10 +1461,10 @@
         }
 
         /**
-         * @return The action of the last injected hover event.
+         * @return The the last injected hover event.
          */
-        public int getLastInjectedHoverAction() {
-            return mLastInjectedHoverEventAction;
+        public MotionEvent getLastInjectedHoverEvent() {
+            return mLastInjectedHoverEvent;
         }
 
         @Override
@@ -1260,6 +1523,8 @@
         private float mLastReceivedUpPointerDownX;
         private float mLastReceivedUpPointerDownY;
 
+        private MotionEvent mLastReceivedEvent;
+
         /**
          * Creates a new instance.
          *
@@ -1294,6 +1559,11 @@
          * @param event The event to process.
          */
         public void onMotionEvent(MotionEvent event) {
+            if (mLastReceivedEvent != null) {
+                mLastReceivedEvent.recycle();
+            }
+            mLastReceivedEvent = MotionEvent.obtain(event);
+
             final int action = event.getActionMasked();
             switch (action) {
                 case MotionEvent.ACTION_DOWN: {
@@ -1318,6 +1588,13 @@
         }
 
         /**
+         * @return The last received event.
+         */
+        public MotionEvent getLastReceivedEvent() {
+            return mLastReceivedEvent;
+        }
+
+        /**
          * @return The number of received pointers that are down.
          */
         public int getReceivedPointerDownCount() {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 76016f4..6464d7f 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4639,35 +4639,12 @@
             pid = tlsIdentity.pid;
         }
 
-        // Root, system server and our own process get to do everything.
-        if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID) {
+        if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
         }
-        // Isolated processes don't get any permissions.
-        if (UserId.isIsolated(uid)) {
-            return PackageManager.PERMISSION_DENIED;
-        }
-        // If there is a uid that owns whatever is being accessed, it has
-        // blanket access to it regardless of the permissions it requires.
-        if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
-        // If the target is not exported, then nobody else can get to it.
-        if (!exported) {
-            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
-            return PackageManager.PERMISSION_DENIED;
-        }
-        if (permission == null) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
-        try {
-            return AppGlobals.getPackageManager()
-                    .checkUidPermission(permission, uid);
-        } catch (RemoteException e) {
-            // Should never happen, but if it does... deny!
-            Slog.e(TAG, "PackageManager is dead?!?", e);
-        }
-        return PackageManager.PERMISSION_DENIED;
+
+        return ActivityManager.checkComponentPermission(permission, uid,
+                owningUid, exported);
     }
 
     /**
@@ -13544,6 +13521,14 @@
         }
     }
 
+    public int getLaunchedFromUid(IBinder activityToken) {
+        ActivityRecord srec = ActivityRecord.forToken(activityToken);
+        if (srec == null) {
+            return -1;
+        }
+        return srec.launchedFromUid;
+    }
+
     // =========================================================
     // LIFETIME MANAGEMENT
     // =========================================================
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 4f1f76f..d85facc 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -16,16 +16,16 @@
 
 package com.android.server.input;
 
-import com.android.internal.os.AtomicFile;
-import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.R;
 import com.android.internal.util.XmlUtils;
 import com.android.server.Watchdog;
 
 import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import android.Manifest;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.content.BroadcastReceiver;
@@ -70,23 +70,19 @@
 import android.view.Surface;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicy;
+import android.widget.Toast;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Map;
+import java.util.HashSet;
 
-import libcore.io.IoUtils;
 import libcore.io.Streams;
 import libcore.util.Objects;
 
@@ -95,11 +91,15 @@
  */
 public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {
     static final String TAG = "InputManager";
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
 
     private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
 
     private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
+    private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
+    private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3;
+    private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
+    private static final int MSG_RELOAD_DEVICE_ALIASES = 5;
 
     // Pointer to native input manager service object.
     private final int mPtr;
@@ -109,6 +109,7 @@
     private final InputManagerHandler mHandler;
     private boolean mSystemReady;
     private BluetoothService mBluetoothService;
+    private NotificationManager mNotificationManager;
 
     // Persistent data store.  Must be locked each time during use.
     private final PersistentDataStore mDataStore = new PersistentDataStore();
@@ -122,6 +123,11 @@
     private final ArrayList<InputDevicesChangedListenerRecord>
             mTempInputDevicesChangedListenersToNotify =
                     new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only
+    private final ArrayList<InputDevice>
+            mTempFullKeyboards = new ArrayList<InputDevice>(); // handler thread only
+    private boolean mKeyboardLayoutNotificationShown;
+    private PendingIntent mKeyboardLayoutIntent;
+    private Toast mSwitchedKeyboardLayoutToast;
 
     // State for vibrator tokens.
     private Object mVibratorLock = new Object();
@@ -235,6 +241,8 @@
             Slog.d(TAG, "System ready.");
         }
         mBluetoothService = bluetoothService;
+        mNotificationManager = (NotificationManager)mContext.getSystemService(
+                Context.NOTIFICATION_SERVICE);
         mSystemReady = true;
 
         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
@@ -244,10 +252,7 @@
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Packages changed, reloading keyboard layouts.");
-                }
-                reloadKeyboardLayouts();
+                updateKeyboardLayouts();
             }
         }, filter, null, mHandler);
 
@@ -255,22 +260,25 @@
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Bluetooth alias changed, reloading device names.");
-                }
                 reloadDeviceAliases();
             }
         }, filter, null, mHandler);
 
-        reloadKeyboardLayouts();
-        reloadDeviceAliases();
+        mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES);
+        mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS);
     }
 
     private void reloadKeyboardLayouts() {
+        if (DEBUG) {
+            Slog.d(TAG, "Reloading keyboard layouts.");
+        }
         nativeReloadKeyboardLayouts(mPtr);
     }
 
     private void reloadDeviceAliases() {
+        if (DEBUG) {
+            Slog.d(TAG, "Reloading device names.");
+        }
         nativeReloadDeviceAliases(mPtr);
     }
 
@@ -558,9 +566,11 @@
     }
 
     // Must be called on handler.
-    private void deliverInputDevicesChanged() {
+    private void deliverInputDevicesChanged(InputDevice[] oldInputDevices) {
+        // Scan for changes.
+        int numFullKeyboardsAdded = 0;
         mTempInputDevicesChangedListenersToNotify.clear();
-
+        mTempFullKeyboards.clear();
         final int numListeners;
         final int[] deviceIdAndGeneration;
         synchronized (mInputDevicesLock) {
@@ -581,13 +591,126 @@
                 final InputDevice inputDevice = mInputDevices[i];
                 deviceIdAndGeneration[i * 2] = inputDevice.getId();
                 deviceIdAndGeneration[i * 2 + 1] = inputDevice.getGeneration();
+
+                if (isFullKeyboard(inputDevice)) {
+                    if (!containsInputDeviceWithDescriptor(oldInputDevices,
+                            inputDevice.getDescriptor())) {
+                        mTempFullKeyboards.add(numFullKeyboardsAdded++, inputDevice);
+                    } else {
+                        mTempFullKeyboards.add(inputDevice);
+                    }
+                }
             }
         }
 
+        // Notify listeners.
         for (int i = 0; i < numListeners; i++) {
             mTempInputDevicesChangedListenersToNotify.get(i).notifyInputDevicesChanged(
                     deviceIdAndGeneration);
         }
+        mTempInputDevicesChangedListenersToNotify.clear();
+
+        // Check for missing keyboard layouts.
+        if (mNotificationManager != null) {
+            final int numFullKeyboards = mTempFullKeyboards.size();
+            boolean missingLayoutForExternalKeyboard = false;
+            boolean missingLayoutForExternalKeyboardAdded = false;
+            synchronized (mDataStore) {
+                for (int i = 0; i < numFullKeyboards; i++) {
+                    final InputDevice inputDevice = mTempFullKeyboards.get(i);
+                    if (mDataStore.getCurrentKeyboardLayout(inputDevice.getDescriptor()) == null) {
+                        missingLayoutForExternalKeyboard = true;
+                        if (i < numFullKeyboardsAdded) {
+                            missingLayoutForExternalKeyboardAdded = true;
+                        }
+                    }
+                }
+            }
+            if (missingLayoutForExternalKeyboard) {
+                if (missingLayoutForExternalKeyboardAdded) {
+                    showMissingKeyboardLayoutNotification();
+                }
+            } else if (mKeyboardLayoutNotificationShown) {
+                hideMissingKeyboardLayoutNotification();
+            }
+        }
+        mTempFullKeyboards.clear();
+    }
+
+    // Must be called on handler.
+    private void showMissingKeyboardLayoutNotification() {
+        if (!mKeyboardLayoutNotificationShown) {
+            if (mKeyboardLayoutIntent == null) {
+                final Intent intent = new Intent("android.settings.INPUT_METHOD_SETTINGS");
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                mKeyboardLayoutIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+            }
+
+            Resources r = mContext.getResources();
+            Notification notification = new Notification.Builder(mContext)
+                    .setContentTitle(r.getString(
+                            R.string.select_keyboard_layout_notification_title))
+                    .setContentText(r.getString(
+                            R.string.select_keyboard_layout_notification_message))
+                    .setContentIntent(mKeyboardLayoutIntent)
+                    .setSmallIcon(R.drawable.ic_settings_language)
+                    .setPriority(Notification.PRIORITY_LOW)
+                    .build();
+            mNotificationManager.notify(R.string.select_keyboard_layout_notification_title,
+                    notification);
+            mKeyboardLayoutNotificationShown = true;
+        }
+    }
+
+    // Must be called on handler.
+    private void hideMissingKeyboardLayoutNotification() {
+        if (mKeyboardLayoutNotificationShown) {
+            mKeyboardLayoutNotificationShown = false;
+            mNotificationManager.cancel(R.string.select_keyboard_layout_notification_title);
+        }
+    }
+
+    // Must be called on handler.
+    private void updateKeyboardLayouts() {
+        // Scan all input devices state for keyboard layouts that have been uninstalled.
+        final HashSet<String> availableKeyboardLayouts = new HashSet<String>();
+        visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
+            @Override
+            public void visitKeyboardLayout(Resources resources,
+                    String descriptor, String label, String collection, int keyboardLayoutResId) {
+                availableKeyboardLayouts.add(descriptor);
+            }
+        });
+        synchronized (mDataStore) {
+            try {
+                mDataStore.removeUninstalledKeyboardLayouts(availableKeyboardLayouts);
+            } finally {
+                mDataStore.saveIfNeeded();
+            }
+        }
+
+        // Reload keyboard layouts.
+        reloadKeyboardLayouts();
+    }
+
+    private static boolean isFullKeyboard(InputDevice inputDevice) {
+        return !inputDevice.isVirtual()
+                && (inputDevice.getSources() & InputDevice.SOURCE_KEYBOARD) != 0
+                && inputDevice.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC;
+    }
+
+    private static boolean containsInputDeviceWithDescriptor(InputDevice[] inputDevices,
+            String descriptor) {
+        final int numDevices = inputDevices.length;
+        for (int i = 0; i < numDevices; i++) {
+            final InputDevice inputDevice = inputDevices[i];
+            if (inputDevice.getDescriptor().equals(descriptor)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     @Override // Binder call
@@ -720,43 +843,147 @@
     }
 
     @Override // Binder call
-    public String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
+    public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
         if (inputDeviceDescriptor == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
 
         synchronized (mDataStore) {
-            return mDataStore.getKeyboardLayout(inputDeviceDescriptor);
+            return mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
         }
     }
 
     @Override // Binder call
-    public void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
             String keyboardLayoutDescriptor) {
         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
-                "setKeyboardLayoutForInputDevice()")) {
+                "setCurrentKeyboardLayoutForInputDevice()")) {
             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
         }
-
         if (inputDeviceDescriptor == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
 
-        final boolean changed;
         synchronized (mDataStore) {
             try {
-                changed = mDataStore.setKeyboardLayout(
-                        inputDeviceDescriptor, keyboardLayoutDescriptor);
+                if (mDataStore.setCurrentKeyboardLayout(
+                        inputDeviceDescriptor, keyboardLayoutDescriptor)) {
+                    mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
+                }
             } finally {
                 mDataStore.saveIfNeeded();
             }
         }
+    }
 
-        if (changed) {
-            if (DEBUG) {
-                Slog.d(TAG, "Keyboard layout changed, reloading keyboard layouts.");
+    @Override // Binder call
+    public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) {
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+
+        synchronized (mDataStore) {
+            return mDataStore.getKeyboardLayouts(inputDeviceDescriptor);
+        }
+    }
+
+    @Override // Binder call
+    public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
+                "addKeyboardLayoutForInputDevice()")) {
+            throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
+        }
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
+
+        synchronized (mDataStore) {
+            try {
+                String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
+                if (mDataStore.addKeyboardLayout(inputDeviceDescriptor, keyboardLayoutDescriptor)
+                        && !Objects.equal(oldLayout,
+                                mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) {
+                    mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
+                }
+            } finally {
+                mDataStore.saveIfNeeded();
             }
-            reloadKeyboardLayouts();
+        }
+    }
+
+    @Override // Binder call
+    public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
+                "removeKeyboardLayoutForInputDevice()")) {
+            throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
+        }
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
+
+        synchronized (mDataStore) {
+            try {
+                String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
+                if (mDataStore.removeKeyboardLayout(inputDeviceDescriptor,
+                        keyboardLayoutDescriptor)
+                        && !Objects.equal(oldLayout,
+                                mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) {
+                    mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
+                }
+            } finally {
+                mDataStore.saveIfNeeded();
+            }
+        }
+    }
+
+    public void switchKeyboardLayout(int deviceId, int direction) {
+        mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, deviceId, direction).sendToTarget();
+    }
+
+    // Must be called on handler.
+    private void handleSwitchKeyboardLayout(int deviceId, int direction) {
+        final InputDevice device = getInputDevice(deviceId);
+        final String inputDeviceDescriptor = device.getDescriptor();
+        if (device != null) {
+            final boolean changed;
+            final String keyboardLayoutDescriptor;
+            synchronized (mDataStore) {
+                try {
+                    changed = mDataStore.switchKeyboardLayout(inputDeviceDescriptor, direction);
+                    keyboardLayoutDescriptor = mDataStore.getCurrentKeyboardLayout(
+                            inputDeviceDescriptor);
+                } finally {
+                    mDataStore.saveIfNeeded();
+                }
+            }
+
+            if (changed) {
+                if (mSwitchedKeyboardLayoutToast != null) {
+                    mSwitchedKeyboardLayoutToast.cancel();
+                    mSwitchedKeyboardLayoutToast = null;
+                }
+                if (keyboardLayoutDescriptor != null) {
+                    KeyboardLayout keyboardLayout = getKeyboardLayout(keyboardLayoutDescriptor);
+                    if (keyboardLayout != null) {
+                        mSwitchedKeyboardLayoutToast = Toast.makeText(
+                                mContext, keyboardLayout.getLabel(), Toast.LENGTH_SHORT);
+                        mSwitchedKeyboardLayoutToast.show();
+                    }
+                }
+
+                reloadKeyboardLayouts();
+            }
         }
     }
 
@@ -978,12 +1205,13 @@
     // Native callback.
     private void notifyInputDevicesChanged(InputDevice[] inputDevices) {
         synchronized (mInputDevicesLock) {
-            mInputDevices = inputDevices;
-
             if (!mInputDevicesChangedPending) {
                 mInputDevicesChangedPending = true;
-                mHandler.sendEmptyMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED);
+                mHandler.obtainMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED,
+                        mInputDevices).sendToTarget();
             }
+
+            mInputDevices = inputDevices;
         }
     }
 
@@ -1132,7 +1360,8 @@
             return null;
         }
 
-        String keyboardLayoutDescriptor = getKeyboardLayoutForInputDevice(inputDeviceDescriptor);
+        String keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(
+                inputDeviceDescriptor);
         if (keyboardLayoutDescriptor == null) {
             return null;
         }
@@ -1203,7 +1432,19 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_DELIVER_INPUT_DEVICES_CHANGED:
-                    deliverInputDevicesChanged();
+                    deliverInputDevicesChanged((InputDevice[])msg.obj);
+                    break;
+                case MSG_SWITCH_KEYBOARD_LAYOUT:
+                    handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
+                    break;
+                case MSG_RELOAD_KEYBOARD_LAYOUTS:
+                    reloadKeyboardLayouts();
+                    break;
+                case MSG_UPDATE_KEYBOARD_LAYOUTS:
+                    updateKeyboardLayouts();
+                    break;
+                case MSG_RELOAD_DEVICE_ALIASES:
+                    reloadDeviceAliases();
                     break;
             }
         }
@@ -1316,186 +1557,4 @@
             onVibratorTokenDied(this);
         }
     }
-
-    /**
-     * Manages persistent state recorded by the input manager service as an XML file.
-     * Caller must acquire lock on the data store before accessing it.
-     *
-     * File format:
-     * <code>
-     * &lt;input-mananger-state>
-     *   &lt;input-devices>
-     *     &lt;input-device descriptor="xxxxx" keyboard-layout="yyyyy" />
-     *   &gt;input-devices>
-     * &gt;/input-manager-state>
-     * </code>
-     */
-    private static final class PersistentDataStore {
-        // Input device state by descriptor.
-        private final HashMap<String, InputDeviceState> mInputDevices =
-                new HashMap<String, InputDeviceState>();
-        private final AtomicFile mAtomicFile;
-
-        // True if the data has been loaded.
-        private boolean mLoaded;
-
-        // True if there are changes to be saved.
-        private boolean mDirty;
-
-        public PersistentDataStore() {
-            mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"));
-        }
-
-        public void saveIfNeeded() {
-            if (mDirty) {
-                save();
-                mDirty = false;
-            }
-        }
-
-        public String getKeyboardLayout(String inputDeviceDescriptor) {
-            InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
-            return state != null ? state.keyboardLayoutDescriptor : null;
-        }
-
-        public boolean setKeyboardLayout(String inputDeviceDescriptor,
-                String keyboardLayoutDescriptor) {
-            InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
-            if (!Objects.equal(state.keyboardLayoutDescriptor, keyboardLayoutDescriptor)) {
-                state.keyboardLayoutDescriptor = keyboardLayoutDescriptor;
-                setDirty();
-                return true;
-            }
-            return false;
-        }
-
-        private InputDeviceState getInputDeviceState(String inputDeviceDescriptor,
-                boolean createIfAbsent) {
-            loadIfNeeded();
-            InputDeviceState state = mInputDevices.get(inputDeviceDescriptor);
-            if (state == null && createIfAbsent) {
-                state = new InputDeviceState();
-                mInputDevices.put(inputDeviceDescriptor, state);
-                setDirty();
-            }
-            return state;
-        }
-
-        private void loadIfNeeded() {
-            if (!mLoaded) {
-                load();
-                mLoaded = true;
-            }
-        }
-
-        private void setDirty() {
-            mDirty = true;
-        }
-
-        private void clearState() {
-            mInputDevices.clear();
-        }
-
-        private void load() {
-            clearState();
-
-            final InputStream is;
-            try {
-                is = mAtomicFile.openRead();
-            } catch (FileNotFoundException ex) {
-                return;
-            }
-
-            XmlPullParser parser;
-            try {
-                parser = Xml.newPullParser();
-                parser.setInput(new BufferedInputStream(is), null);
-                loadFromXml(parser);
-            } catch (IOException ex) {
-                Slog.w(TAG, "Failed to load input manager persistent store data.", ex);
-                clearState();
-            } catch (XmlPullParserException ex) {
-                Slog.w(TAG, "Failed to load input manager persistent store data.", ex);
-                clearState();
-            } finally {
-                IoUtils.closeQuietly(is);
-            }
-        }
-
-        private void save() {
-            final FileOutputStream os;
-            try {
-                os = mAtomicFile.startWrite();
-                boolean success = false;
-                try {
-                    XmlSerializer serializer = new FastXmlSerializer();
-                    serializer.setOutput(new BufferedOutputStream(os), "utf-8");
-                    saveToXml(serializer);
-                    serializer.flush();
-                    success = true;
-                } finally {
-                    if (success) {
-                        mAtomicFile.finishWrite(os);
-                    } else {
-                        mAtomicFile.failWrite(os);
-                    }
-                }
-            } catch (IOException ex) {
-                Slog.w(TAG, "Failed to save input manager persistent store data.", ex);
-            }
-        }
-
-        private void loadFromXml(XmlPullParser parser)
-                throws IOException, XmlPullParserException {
-            XmlUtils.beginDocument(parser, "input-manager-state");
-            final int outerDepth = parser.getDepth();
-            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-                if (parser.getName().equals("input-devices")) {
-                    loadInputDevicesFromXml(parser);
-                }
-            }
-        }
-
-        private void loadInputDevicesFromXml(XmlPullParser parser)
-                throws IOException, XmlPullParserException {
-            final int outerDepth = parser.getDepth();
-            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-                if (parser.getName().equals("input-device")) {
-                    String descriptor = parser.getAttributeValue(null, "descriptor");
-                    if (descriptor == null) {
-                        throw new XmlPullParserException(
-                                "Missing descriptor attribute on input-device");
-                    }
-                    InputDeviceState state = new InputDeviceState();
-                    state.keyboardLayoutDescriptor =
-                            parser.getAttributeValue(null, "keyboard-layout");
-                    mInputDevices.put(descriptor, state);
-                }
-            }
-        }
-
-        private void saveToXml(XmlSerializer serializer) throws IOException {
-            serializer.startDocument(null, true);
-            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-            serializer.startTag(null, "input-manager-state");
-            serializer.startTag(null, "input-devices");
-            for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) {
-                final String descriptor = entry.getKey();
-                final InputDeviceState state = entry.getValue();
-                serializer.startTag(null, "input-device");
-                serializer.attribute(null, "descriptor", descriptor);
-                if (state.keyboardLayoutDescriptor != null) {
-                    serializer.attribute(null, "keyboard-layout", state.keyboardLayoutDescriptor);
-                }
-                serializer.endTag(null, "input-device");
-            }
-            serializer.endTag(null, "input-devices");
-            serializer.endTag(null, "input-manager-state");
-            serializer.endDocument();
-        }
-    }
-
-    private static final class InputDeviceState {
-        public String keyboardLayoutDescriptor;
-    }
 }
diff --git a/services/java/com/android/server/input/PersistentDataStore.java b/services/java/com/android/server/input/PersistentDataStore.java
new file mode 100644
index 0000000..fbe3e8b
--- /dev/null
+++ b/services/java/com/android/server/input/PersistentDataStore.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2012 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 com.android.server.input;
+
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.util.Slog;
+import android.util.Xml;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import libcore.io.IoUtils;
+import libcore.util.Objects;
+
+/**
+ * Manages persistent state recorded by the input manager service as an XML file.
+ * Caller must acquire lock on the data store before accessing it.
+ *
+ * File format:
+ * <code>
+ * &lt;input-mananger-state>
+ *   &lt;input-devices>
+ *     &lt;input-device descriptor="xxxxx" keyboard-layout="yyyyy" />
+ *   &gt;input-devices>
+ * &gt;/input-manager-state>
+ * </code>
+ */
+final class PersistentDataStore {
+    static final String TAG = "InputManager";
+
+    // Input device state by descriptor.
+    private final HashMap<String, InputDeviceState> mInputDevices =
+            new HashMap<String, InputDeviceState>();
+    private final AtomicFile mAtomicFile;
+
+    // True if the data has been loaded.
+    private boolean mLoaded;
+
+    // True if there are changes to be saved.
+    private boolean mDirty;
+
+    public PersistentDataStore() {
+        mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"));
+    }
+
+    public void saveIfNeeded() {
+        if (mDirty) {
+            save();
+            mDirty = false;
+        }
+    }
+
+    public String getCurrentKeyboardLayout(String inputDeviceDescriptor) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
+        return state != null ? state.getCurrentKeyboardLayout() : null;
+    }
+
+    public boolean setCurrentKeyboardLayout(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
+        if (state.setCurrentKeyboardLayout(keyboardLayoutDescriptor)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public String[] getKeyboardLayouts(String inputDeviceDescriptor) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
+        if (state == null) {
+            return (String[])ArrayUtils.emptyArray(String.class);
+        }
+        return state.getKeyboardLayouts();
+    }
+
+    public boolean addKeyboardLayout(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
+        if (state.addKeyboardLayout(keyboardLayoutDescriptor)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public boolean removeKeyboardLayout(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
+        if (state.removeKeyboardLayout(keyboardLayoutDescriptor)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public boolean switchKeyboardLayout(String inputDeviceDescriptor, int direction) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
+        if (state != null && state.switchKeyboardLayout(direction)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
+        boolean changed = false;
+        for (InputDeviceState state : mInputDevices.values()) {
+            if (state.removeUninstalledKeyboardLayouts(availableKeyboardLayouts)) {
+                changed = true;
+            }
+        }
+        if (changed) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    private InputDeviceState getInputDeviceState(String inputDeviceDescriptor,
+            boolean createIfAbsent) {
+        loadIfNeeded();
+        InputDeviceState state = mInputDevices.get(inputDeviceDescriptor);
+        if (state == null && createIfAbsent) {
+            state = new InputDeviceState();
+            mInputDevices.put(inputDeviceDescriptor, state);
+            setDirty();
+        }
+        return state;
+    }
+
+    private void loadIfNeeded() {
+        if (!mLoaded) {
+            load();
+            mLoaded = true;
+        }
+    }
+
+    private void setDirty() {
+        mDirty = true;
+    }
+
+    private void clearState() {
+        mInputDevices.clear();
+    }
+
+    private void load() {
+        clearState();
+
+        final InputStream is;
+        try {
+            is = mAtomicFile.openRead();
+        } catch (FileNotFoundException ex) {
+            return;
+        }
+
+        XmlPullParser parser;
+        try {
+            parser = Xml.newPullParser();
+            parser.setInput(new BufferedInputStream(is), null);
+            loadFromXml(parser);
+        } catch (IOException ex) {
+            Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex);
+            clearState();
+        } catch (XmlPullParserException ex) {
+            Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex);
+            clearState();
+        } finally {
+            IoUtils.closeQuietly(is);
+        }
+    }
+
+    private void save() {
+        final FileOutputStream os;
+        try {
+            os = mAtomicFile.startWrite();
+            boolean success = false;
+            try {
+                XmlSerializer serializer = new FastXmlSerializer();
+                serializer.setOutput(new BufferedOutputStream(os), "utf-8");
+                saveToXml(serializer);
+                serializer.flush();
+                success = true;
+            } finally {
+                if (success) {
+                    mAtomicFile.finishWrite(os);
+                } else {
+                    mAtomicFile.failWrite(os);
+                }
+            }
+        } catch (IOException ex) {
+            Slog.w(InputManagerService.TAG, "Failed to save input manager persistent store data.", ex);
+        }
+    }
+
+    private void loadFromXml(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        XmlUtils.beginDocument(parser, "input-manager-state");
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (parser.getName().equals("input-devices")) {
+                loadInputDevicesFromXml(parser);
+            }
+        }
+    }
+
+    private void loadInputDevicesFromXml(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (parser.getName().equals("input-device")) {
+                String descriptor = parser.getAttributeValue(null, "descriptor");
+                if (descriptor == null) {
+                    throw new XmlPullParserException(
+                            "Missing descriptor attribute on input-device.");
+                }
+                if (mInputDevices.containsKey(descriptor)) {
+                    throw new XmlPullParserException("Found duplicate input device.");
+                }
+
+                InputDeviceState state = new InputDeviceState();
+                state.loadFromXml(parser);
+                mInputDevices.put(descriptor, state);
+            }
+        }
+    }
+
+    private void saveToXml(XmlSerializer serializer) throws IOException {
+        serializer.startDocument(null, true);
+        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+        serializer.startTag(null, "input-manager-state");
+        serializer.startTag(null, "input-devices");
+        for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) {
+            final String descriptor = entry.getKey();
+            final InputDeviceState state = entry.getValue();
+            serializer.startTag(null, "input-device");
+            serializer.attribute(null, "descriptor", descriptor);
+            state.saveToXml(serializer);
+            serializer.endTag(null, "input-device");
+        }
+        serializer.endTag(null, "input-devices");
+        serializer.endTag(null, "input-manager-state");
+        serializer.endDocument();
+    }
+
+    private static final class InputDeviceState {
+        private String mCurrentKeyboardLayout;
+        private ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
+
+        public String getCurrentKeyboardLayout() {
+            return mCurrentKeyboardLayout;
+        }
+
+        public boolean setCurrentKeyboardLayout(String keyboardLayout) {
+            if (Objects.equal(mCurrentKeyboardLayout, keyboardLayout)) {
+                return false;
+            }
+            addKeyboardLayout(keyboardLayout);
+            mCurrentKeyboardLayout = keyboardLayout;
+            return true;
+        }
+
+        public String[] getKeyboardLayouts() {
+            if (mKeyboardLayouts.isEmpty()) {
+                return (String[])ArrayUtils.emptyArray(String.class);
+            }
+            return mKeyboardLayouts.toArray(new String[mKeyboardLayouts.size()]);
+        }
+
+        public boolean addKeyboardLayout(String keyboardLayout) {
+            int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
+            if (index >= 0) {
+                return false;
+            }
+            mKeyboardLayouts.add(-index - 1, keyboardLayout);
+            if (mCurrentKeyboardLayout == null) {
+                mCurrentKeyboardLayout = keyboardLayout;
+            }
+            return true;
+        }
+
+        public boolean removeKeyboardLayout(String keyboardLayout) {
+            int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
+            if (index < 0) {
+                return false;
+            }
+            mKeyboardLayouts.remove(index);
+            updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, index);
+            return true;
+        }
+
+        private void updateCurrentKeyboardLayoutIfRemoved(
+                String removedKeyboardLayout, int removedIndex) {
+            if (Objects.equal(mCurrentKeyboardLayout, removedKeyboardLayout)) {
+                if (!mKeyboardLayouts.isEmpty()) {
+                    int index = removedIndex;
+                    if (index == mKeyboardLayouts.size()) {
+                        index = 0;
+                    }
+                    mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
+                } else {
+                    mCurrentKeyboardLayout = null;
+                }
+            }
+        }
+
+        public boolean switchKeyboardLayout(int direction) {
+            final int size = mKeyboardLayouts.size();
+            if (size < 2) {
+                return false;
+            }
+            int index = Collections.binarySearch(mKeyboardLayouts, mCurrentKeyboardLayout);
+            assert index >= 0;
+            if (direction > 0) {
+                index = (index + 1) % size;
+            } else {
+                index = (index + size - 1) % size;
+            }
+            mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
+            return true;
+        }
+
+        public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
+            boolean changed = false;
+            for (int i = mKeyboardLayouts.size(); i-- > 0; ) {
+                String keyboardLayout = mKeyboardLayouts.get(i);
+                if (!availableKeyboardLayouts.contains(keyboardLayout)) {
+                    Slog.i(TAG, "Removing uninstalled keyboard layout " + keyboardLayout);
+                    mKeyboardLayouts.remove(i);
+                    updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, i);
+                    changed = true;
+                }
+            }
+            return changed;
+        }
+
+        public void loadFromXml(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            final int outerDepth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                if (parser.getName().equals("keyboard-layout")) {
+                    String descriptor = parser.getAttributeValue(null, "descriptor");
+                    if (descriptor == null) {
+                        throw new XmlPullParserException(
+                                "Missing descriptor attribute on keyboard-layout.");
+                    }
+                    String current = parser.getAttributeValue(null, "current");
+                    if (mKeyboardLayouts.contains(descriptor)) {
+                        throw new XmlPullParserException(
+                                "Found duplicate keyboard layout.");
+                    }
+
+                    mKeyboardLayouts.add(descriptor);
+                    if (current != null && current.equals("true")) {
+                        if (mCurrentKeyboardLayout != null) {
+                            throw new XmlPullParserException(
+                                    "Found multiple current keyboard layouts.");
+                        }
+                        mCurrentKeyboardLayout = descriptor;
+                    }
+                }
+            }
+
+            // Maintain invariant that layouts are sorted.
+            Collections.sort(mKeyboardLayouts);
+
+            // Maintain invariant that there is always a current keyboard layout unless
+            // there are none installed.
+            if (mCurrentKeyboardLayout == null && !mKeyboardLayouts.isEmpty()) {
+                mCurrentKeyboardLayout = mKeyboardLayouts.get(0);
+            }
+        }
+
+        public void saveToXml(XmlSerializer serializer) throws IOException {
+            for (String layout : mKeyboardLayouts) {
+                serializer.startTag(null, "keyboard-layout");
+                serializer.attribute(null, "descriptor", layout);
+                if (layout.equals(mCurrentKeyboardLayout)) {
+                    serializer.attribute(null, "current", "true");
+                }
+                serializer.endTag(null, "keyboard-layout");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 480992b..cf3a5d2 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -538,8 +538,16 @@
         if (mDimAnimator == null) {
             mDimAnimator = new DimAnimator(mService.mFxSession);
         }
-        mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS,
-                new DimAnimator.Parameters(winAnimator, width, height, target)));
+        // Only set dim params on the highest dimmed layer.
+        final WindowStateAnimator dimWinAnimator = mDimParams == null
+                ? null : mDimParams.mDimWinAnimator;
+        // Don't turn on for an unshown surface, or for any layer but the highest dimmed one.
+        if (winAnimator.mSurfaceShown &&
+                (dimWinAnimator == null || !dimWinAnimator.mSurfaceShown
+                || dimWinAnimator.mAnimLayer < winAnimator.mAnimLayer)) {
+            mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS,
+                    new DimAnimator.Parameters(winAnimator, width, height, target)));
+        }
     }
 
     // TODO(cmautner): Move into Handler
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 076ba9a..fbf9256 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5007,6 +5007,12 @@
 
     // Called by window manager policy.  Not exposed externally.
     @Override
+    public void switchKeyboardLayout(int deviceId, int direction) {
+        mInputManager.switchKeyboardLayout(deviceId, direction);
+    }
+
+    // Called by window manager policy.  Not exposed externally.
+    @Override
     public void shutdown() {
         ShutdownThread.shutdown(mContext, true);
     }
@@ -6561,6 +6567,16 @@
         sendScreenStatusToClients();
     }
 
+    public IBinder getFocusedWindowClientToken() {
+        synchronized (mWindowMap) {
+            WindowState windowState = getFocusedWindowLocked();
+            if (windowState != null) {
+                return windowState.mClient.asBinder();
+            }
+            return null;
+        }
+    }
+
     private WindowState getFocusedWindow() {
         synchronized (mWindowMap) {
             return getFocusedWindowLocked();
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index d738d7b..fcf2f92 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -579,10 +579,14 @@
             mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
         }
 
-        // Call onReady only when SIM or RUIM card becomes ready (not NV)
+        // Call onReady Record(s) on the IccCard becomes ready (not NV)
         if (oldState != State.READY && newState == State.READY &&
                 (is3gpp || isSubscriptionFromIccCard)) {
-            mIccFileHandler.setAid(getAid());
+            if (!(mIccFileHandler instanceof CdmaLteUiccFileHandler)) {
+                // CdmaLteUicc File Handler deals with both USIM and CSIM.
+                // Do not lock onto one AID for now.
+                mIccFileHandler.setAid(getAid());
+            }
             mIccRecords.onReady();
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index 69dd666..e4cfb23 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -146,6 +146,7 @@
         "ci", // Cote d'Ivoire
         "eh", // Western Sahara
         "fo", // Faroe Islands, Denmark
+        "gb", // United Kingdom of Great Britain and Northern Ireland
         "gh", // Ghana
         "gm", // Gambia
         "gn", // Guinea
@@ -161,7 +162,6 @@
         "sn", // Senegal
         "st", // Sao Tome and Principe
         "tg", // Togo
-        "uk", // U.K
     };
 
     /** Reason for registration denial. */
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index e4287c0..25cd112 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -293,8 +293,15 @@
                         EVENT_RUIM_RECORDS_LOADED, null);
                 mNeedToRegForRuimLoaded = false;
             }
-            if (DBG) log("Receive EVENT_RUIM_READY and Send Request getCDMASubscription.");
-            getSubscriptionInfoAndStartPollingThreads();
+
+            if (phone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
+                // Subscription will be read from SIM I/O
+                if (DBG) log("Receive EVENT_RUIM_READY");
+                pollState();
+            } else {
+                if (DBG) log("Receive EVENT_RUIM_READY and Send Request getCDMASubscription.");
+                getSubscriptionInfoAndStartPollingThreads();
+            }
             phone.prepareEri();
             break;
 
@@ -878,19 +885,22 @@
             // For NITZ string without time zone,
             // need adjust time to reflect default time zone setting
             zone = TimeZone.getDefault();
-            long ctm = System.currentTimeMillis();
-            long tzOffset = zone.getOffset(ctm);
-            if (DBG) {
-                log("fixTimeZone: tzOffset=" + tzOffset + " ltod=" + TimeUtils.logTimeOfDay(ctm));
-            }
-            if (getAutoTime()) {
-                long adj = ctm - tzOffset;
-                if (DBG) log("fixTimeZone: adj ltod=" + TimeUtils.logTimeOfDay(adj));
-                setAndBroadcastNetworkSetTime(adj);
-            } else {
-                // Adjust the saved NITZ time to account for tzOffset.
-                mSavedTime = mSavedTime - tzOffset;
-                if (DBG) log("fixTimeZone: adj mSavedTime=" + mSavedTime);
+            if (mNeedFixZone) {
+                long ctm = System.currentTimeMillis();
+                long tzOffset = zone.getOffset(ctm);
+                if (DBG) {
+                    log("fixTimeZone: tzOffset=" + tzOffset +
+                            " ltod=" + TimeUtils.logTimeOfDay(ctm));
+                }
+                if (getAutoTime()) {
+                    long adj = ctm - tzOffset;
+                    if (DBG) log("fixTimeZone: adj ltod=" + TimeUtils.logTimeOfDay(adj));
+                    setAndBroadcastNetworkSetTime(adj);
+                } else {
+                    // Adjust the saved NITZ time to account for tzOffset.
+                    mSavedTime = mSavedTime - tzOffset;
+                    if (DBG) log("fixTimeZone: adj mSavedTime=" + mSavedTime);
+                }
             }
             if (DBG) log("fixTimeZone: using default TimeZone");
         } else if (isoCountryCode.equals("")) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 1aa17c7..b7569da 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -111,7 +111,7 @@
      * are in. Keep the time zone information from the NITZ string so
      * we can fix the time zone once know the country.
      */
-    private boolean mNeedFixZone = false;
+    private boolean mNeedFixZoneAfterNitz = false;
     private int mZoneOffset;
     private boolean mZoneDst;
     private long mZoneTime;
@@ -906,7 +906,7 @@
                 }
 
                 if (shouldFixTimeZoneNow(phone, operatorNumeric, prevOperatorNumeric,
-                        mNeedFixZone)) {
+                        mNeedFixZoneAfterNitz)) {
                     // If the offset is (0, false) and the timezone property
                     // is set, use the timezone property rather than
                     // GMT.
@@ -917,25 +917,35 @@
                             " iso-cc='" + iso +
                             "' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, iso));
                     }
+
+                    // "(mZoneOffset == 0) && (mZoneDst == false) &&
+                    //  (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)"
+                    // means that we received a NITZ string telling
+                    // it is in GMT+0 w/ DST time zone
+                    // BUT iso tells is NOT, e.g, a wrong NITZ reporting
+                    // local time w/ 0 offset.
                     if ((mZoneOffset == 0) && (mZoneDst == false) &&
                         (zoneName != null) && (zoneName.length() > 0) &&
                         (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) {
                         zone = TimeZone.getDefault();
-                        // For NITZ string without timezone,
-                        // need adjust time to reflect default timezone setting
-                        long ctm = System.currentTimeMillis();
-                        long tzOffset = zone.getOffset(ctm);
-                        if (DBG) {
-                            log("pollStateDone: tzOffset=" + tzOffset + " ltod=" +
+                        if (mNeedFixZoneAfterNitz) {
+                            // For wrong NITZ reporting local time w/ 0 offset,
+                            // need adjust time to reflect default timezone setting
+                            long ctm = System.currentTimeMillis();
+                            long tzOffset = zone.getOffset(ctm);
+                            if (DBG) {
+                                log("pollStateDone: tzOffset=" + tzOffset + " ltod=" +
                                         TimeUtils.logTimeOfDay(ctm));
-                        }
-                        if (getAutoTime()) {
-                            long adj = ctm - tzOffset;
-                            if (DBG) log("pollStateDone: adj ltod=" + TimeUtils.logTimeOfDay(adj));
-                            setAndBroadcastNetworkSetTime(adj);
-                        } else {
-                            // Adjust the saved NITZ time to account for tzOffset.
-                            mSavedTime = mSavedTime - tzOffset;
+                            }
+                            if (getAutoTime()) {
+                                long adj = ctm - tzOffset;
+                                if (DBG) log("pollStateDone: adj ltod=" +
+                                        TimeUtils.logTimeOfDay(adj));
+                                setAndBroadcastNetworkSetTime(adj);
+                            } else {
+                                // Adjust the saved NITZ time to account for tzOffset.
+                                mSavedTime = mSavedTime - tzOffset;
+                            }
                         }
                         if (DBG) log("pollStateDone: using default TimeZone");
                     } else if (iso.equals("")){
@@ -948,7 +958,7 @@
                         if (DBG) log("pollStateDone: using getTimeZone(off, dst, time, iso)");
                     }
 
-                    mNeedFixZone = false;
+                    mNeedFixZoneAfterNitz = false;
 
                     if (zone != null) {
                         log("pollStateDone: zone != null zone.getID=" + zone.getID());
@@ -1440,7 +1450,7 @@
                 // so we don't know how to identify the DST rules yet.  Save
                 // the information and hope to fix it up later.
 
-                mNeedFixZone = true;
+                mNeedFixZoneAfterNitz = true;
                 mZoneOffset  = tzOffset;
                 mZoneDst     = dst != 0;
                 mZoneTime    = c.getTimeInMillis();
@@ -1696,7 +1706,7 @@
         pw.println(" mGsmRoaming=" + mGsmRoaming);
         pw.println(" mDataRoaming=" + mDataRoaming);
         pw.println(" mEmergencyOnly=" + mEmergencyOnly);
-        pw.println(" mNeedFixZone=" + mNeedFixZone);
+        pw.println(" mNeedFixZoneAfterNitz=" + mNeedFixZoneAfterNitz);
         pw.println(" mZoneOffset=" + mZoneOffset);
         pw.println(" mZoneDst=" + mZoneDst);
         pw.println(" mZoneTime=" + mZoneTime);
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index ec61403..73705ae 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -56,55 +56,92 @@
     return true;
 }
 
+// The default to use if no other ignore pattern is defined.
+const char * const gDefaultIgnoreAssets =
+    "!.svn:!.git:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~";
+// The ignore pattern that can be passed via --ignore-assets in Main.cpp
+const char * gUserIgnoreAssets = NULL;
+
 static bool isHidden(const char *root, const char *path)
 {
-    const char *ext  = NULL;
-    const char *type = NULL;
+    // Patterns syntax:
+    // - Delimiter is :
+    // - Entry can start with the flag ! to avoid printing a warning
+    //   about the file being ignored.
+    // - Entry can have the flag "<dir>" to match only directories
+    //   or <file> to match only files. Default is to match both.
+    // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
+    //   where prefix/suffix must have at least 1 character (so that
+    //   we don't match a '*' catch-all pattern.)
+    // - The special filenames "." and ".." are always ignored.
+    // - Otherwise the full string is matched.
+    // - match is not case-sensitive.
 
-    // Skip all hidden files.
-    if (path[0] == '.') {
-        // Skip ., .. and  .svn but don't chatter about it.
-        if (strcmp(path, ".") == 0
-            || strcmp(path, "..") == 0
-            || strcmp(path, ".svn") == 0) {
-            return true;
-        }
-        type = "hidden";
-    } else if (path[0] == '_') {
-        // skip directories starting with _ (don't chatter about it)
-        String8 subdirName(root);
-        subdirName.appendPath(path);
-        if (getFileType(subdirName.string()) == kFileTypeDirectory) {
-            return true;
-        }
-    } else if (strcmp(path, "CVS") == 0) {
-        // Skip CVS but don't chatter about it.
+    if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
         return true;
-    } else if (strcasecmp(path, "thumbs.db") == 0
-               || strcasecmp(path, "picasa.ini") == 0) {
-        // Skip suspected image indexes files.
-        type = "index";
-    } else if (path[strlen(path)-1] == '~') {
-        // Skip suspected emacs backup files.
-        type = "backup";
-    } else if ((ext = strrchr(path, '.')) != NULL && strcmp(ext, ".scc") == 0) {
-        // Skip VisualSourceSafe files and don't chatter about it
-        return true;
-    } else {
-        // Let everything else through.
-        return false;
     }
 
-    /* If we get this far, "type" should be set and the file
-     * should be skipped.
-     */
-    String8 subdirName(root);
-    subdirName.appendPath(path);
-    fprintf(stderr, "    (skipping %s %s '%s')\n", type,
-            getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file",
-            subdirName.string());
+    const char *delim = ":";
+    const char *p = gUserIgnoreAssets;
+    if (!p || !p[0]) {
+        p = getenv("ANDROID_AAPT_IGNORE");
+    }
+    if (!p || !p[0]) {
+        p = gDefaultIgnoreAssets;
+    }
+    char *patterns = strdup(p);
 
-    return true;
+    bool ignore = false;
+    bool chatty = true;
+    char *matchedPattern = NULL;
+
+    String8 fullPath(root);
+    fullPath.appendPath(path);
+    FileType type = getFileType(fullPath);
+
+    int plen = strlen(path);
+
+    // Note: we don't have strtok_r under mingw.
+    for(char *token = strtok(patterns, delim);
+            !ignore && token != NULL;
+            token = strtok(NULL, delim)) {
+        chatty = token[0] != '!';
+        if (!chatty) token++; // skip !
+        if (strncasecmp(token, "<dir>" , 5) == 0) {
+            if (type != kFileTypeDirectory) continue;
+            token += 5;
+        }
+        if (strncasecmp(token, "<file>", 6) == 0) {
+            if (type != kFileTypeRegular) continue;
+            token += 6;
+        }
+
+        matchedPattern = token;
+        int n = strlen(token);
+
+        if (token[0] == '*') {
+            // Match *suffix
+            token++;
+            if (n <= plen) {
+                ignore = strncasecmp(token, path + plen - n, n) == 0;
+            }
+        } else if (n > 1 && token[n - 1] == '*') {
+            // Match prefix*
+            ignore = strncasecmp(token, path, n - 1) == 0;
+        } else {
+            ignore = strcasecmp(token, path) == 0;
+        }
+    }
+
+    if (ignore && chatty) {
+        fprintf(stderr, "    (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
+                type == kFileTypeDirectory ? "dir" : "file",
+                path,
+                matchedPattern ? matchedPattern : "");
+    }
+
+    free(patterns);
+    return ignore;
 }
 
 // =========================================================================
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 5924952..d5f296c 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -22,6 +22,10 @@
 
 using namespace android;
 
+
+extern const char * const gDefaultIgnoreAssets;
+extern const char * gUserIgnoreAssets;
+
 bool valid_symbol_name(const String8& str);
 
 class AaptAssets;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 50c828d..9f05c6a 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -178,7 +178,11 @@
         "   --non-constant-id\n"
         "       Make the resources ID non constant. This is required to make an R java class\n"
         "       that does not contain the final value but is used to make reusable compiled\n"
-        "       libraries that need to access resources.\n");
+        "       libraries that need to access resources.\n"
+        "   --ignore-assets\n"
+        "       Assets to be ignored. Default pattern is:\n"
+        "       %s\n",
+        gDefaultIgnoreAssets);
 }
 
 /*
@@ -556,7 +560,16 @@
                     bundle.setNonConstantId(true);
                 } else if (strcmp(cp, "-no-crunch") == 0) {
                     bundle.setUseCrunchCache(true);
-                }else {
+                } else if (strcmp(cp, "-ignore-assets") == 0) {
+                    argc--;
+                    argv++;
+                    if (!argc) {
+                        fprintf(stderr, "ERROR: No argument supplied for '--ignore-assets' option\n");
+                        wantUsage = true;
+                        goto bail;
+                    }
+                    gUserIgnoreAssets = argv[0];
+                } else {
                     fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
                     wantUsage = true;
                     goto bail;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index b9ec30c..a69adc1 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -2089,6 +2089,23 @@
     keep->add(rule, location);
 }
 
+void
+addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName,
+        const char* pkg, const String8& srcName, int line)
+{
+    String8 rule("-keepclassmembers class * { *** ");
+    rule += memberName;
+    rule += "(...); }";
+
+    String8 location("onClick ");
+    location += srcName;
+    char lineno[20];
+    sprintf(lineno, ":%d", line);
+    location += lineno;
+
+    keep->add(rule, location);
+}
+
 status_t
 writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
 {
@@ -2251,6 +2268,13 @@
                 }
             }
         }
+        ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick");
+        if (attrIndex >= 0) {
+            size_t len;
+            addProguardKeepMethodRule(keep,
+                                String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
+                                layoutFile->getPrintableSource(), tree.getLineNumber());
+        }
     }
 
     return NO_ERROR;
@@ -2289,6 +2313,9 @@
         } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
             startTag = "PreferenceScreen";
             tagAttrPairs = &kXmlTagAttrPairs;
+        } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
+            startTag = "menu";
+            tagAttrPairs = NULL;
         } else {
             continue;
         }