Merge "Revert "Remove permission android.permission.WRITE_MEDIA_STORAGE""
diff --git a/Android.mk b/Android.mk
index cd9ae7d..898d7e2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -158,6 +158,7 @@
 	core/java/com/android/internal/view/IInputMethodSession.aidl \
 	core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
 	core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
+	keystore/java/android/security/IKeyChainService.aidl \
 	location/java/android/location/ICountryDetector.aidl \
 	location/java/android/location/ICountryListener.aidl \
 	location/java/android/location/IGeocodeProvider.aidl \
diff --git a/api/13.txt b/api/13.txt
index cbc2c40..a17b80c 100644
--- a/api/13.txt
+++ b/api/13.txt
@@ -21945,49 +21945,24 @@
 
 package android.view.accessibility {
 
-  public final class AccessibilityEvent implements android.os.Parcelable {
+  public final class AccessibilityEvent extends android.view.accessibility.AccessibilityRecord implements android.os.Parcelable {
+    method public void appendRecord(android.view.accessibility.AccessibilityRecord);
     method public int describeContents();
-    method public int getAddedCount();
-    method public java.lang.CharSequence getBeforeText();
-    method public java.lang.CharSequence getClassName();
-    method public java.lang.CharSequence getContentDescription();
-    method public int getCurrentItemIndex();
     method public long getEventTime();
     method public int getEventType();
-    method public int getFromIndex();
-    method public int getItemCount();
     method public java.lang.CharSequence getPackageName();
-    method public android.os.Parcelable getParcelableData();
-    method public int getRemovedCount();
-    method public java.util.List<java.lang.CharSequence> getText();
+    method public android.view.accessibility.AccessibilityRecord getRecord(int);
+    method public int getRecordCount();
     method public void initFromParcel(android.os.Parcel);
-    method public boolean isChecked();
-    method public boolean isEnabled();
-    method public boolean isFullScreen();
-    method public boolean isPassword();
     method public static android.view.accessibility.AccessibilityEvent obtain(int);
     method public static android.view.accessibility.AccessibilityEvent obtain();
-    method public void recycle();
-    method public void setAddedCount(int);
-    method public void setBeforeText(java.lang.CharSequence);
-    method public void setChecked(boolean);
-    method public void setClassName(java.lang.CharSequence);
-    method public void setContentDescription(java.lang.CharSequence);
-    method public void setCurrentItemIndex(int);
-    method public void setEnabled(boolean);
     method public void setEventTime(long);
     method public void setEventType(int);
-    method public void setFromIndex(int);
-    method public void setFullScreen(boolean);
-    method public void setItemCount(int);
     method public void setPackageName(java.lang.CharSequence);
-    method public void setParcelableData(android.os.Parcelable);
-    method public void setPassword(boolean);
-    method public void setRemovedCount(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int INVALID_POSITION = -1; // 0xffffffff
-    field public static final int MAX_TEXT_LENGTH = 500; // 0x1f4
+    field public static final deprecated int MAX_TEXT_LENGTH = 500; // 0x1f4
     field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
     field public static final int TYPE_NOTIFICATION_STATE_CHANGED = 64; // 0x40
     field public static final int TYPE_VIEW_CLICKED = 1; // 0x1
@@ -22010,6 +21985,51 @@
     method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
   }
 
+  public class AccessibilityRecord {
+    ctor protected AccessibilityRecord();
+    method protected void clear();
+    method public int getAddedCount();
+    method public java.lang.CharSequence getBeforeText();
+    method public boolean getBooleanProperty(int);
+    method public java.lang.CharSequence getClassName();
+    method public java.lang.CharSequence getContentDescription();
+    method public int getCurrentItemIndex();
+    method public int getFromIndex();
+    method public int getItemCount();
+    method public android.os.Parcelable getParcelableData();
+    method public int getRemovedCount();
+    method public java.util.List<java.lang.CharSequence> getText();
+    method public boolean isChecked();
+    method public boolean isEnabled();
+    method public boolean isFullScreen();
+    method public boolean isPassword();
+    method protected static android.view.accessibility.AccessibilityRecord obtain();
+    method public void recycle();
+    method public void setAddedCount(int);
+    method public void setBeforeText(java.lang.CharSequence);
+    method public void setChecked(boolean);
+    method public void setClassName(java.lang.CharSequence);
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setCurrentItemIndex(int);
+    method public void setEnabled(boolean);
+    method public void setFromIndex(int);
+    method public void setFullScreen(boolean);
+    method public void setItemCount(int);
+    method public void setParcelableData(android.os.Parcelable);
+    method public void setPassword(boolean);
+    method public void setRemovedCount(int);
+    field protected int mAddedCount;
+    field protected java.lang.CharSequence mBeforeText;
+    field protected int mBooleanProperties;
+    field protected java.lang.CharSequence mClassName;
+    field protected java.lang.CharSequence mContentDescription;
+    field protected int mCurrentItemIndex;
+    field protected int mFromIndex;
+    field protected int mItemCount;
+    field protected android.os.Parcelable mParcelableData;
+    field protected int mRemovedCount;
+    field protected final java.util.List mText;
+  }
 }
 
 package android.view.animation {
diff --git a/api/current.txt b/api/current.txt
index c7e6ee6..fd05e48 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21174,6 +21174,7 @@
     method protected void onLayout(boolean, int, int, int, int);
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
+    method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method protected void onRestoreInstanceState(android.os.Parcelable);
     method protected android.os.Parcelable onSaveInstanceState();
     method protected void onScrollChanged(int, int, int, int);
@@ -21585,6 +21586,7 @@
     method public boolean onInterceptTouchEvent(android.view.MotionEvent);
     method protected abstract void onLayout(boolean, int, int, int, int);
     method protected boolean onRequestFocusInDescendants(int, android.graphics.Rect);
+    method public boolean onRequestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public void recomputeViewAttributes(android.view.View);
     method public void removeAllViews();
     method public void removeAllViewsInLayout();
@@ -21597,6 +21599,7 @@
     method public void requestChildFocus(android.view.View, android.view.View);
     method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
     method public void requestDisallowInterceptTouchEvent(boolean);
+    method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public void requestTransparentRegion(android.view.View);
     method public void scheduleLayoutAnimation();
     method public void setAddStatesFromChildren(boolean);
@@ -21683,6 +21686,7 @@
     method public abstract boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
     method public abstract void requestDisallowInterceptTouchEvent(boolean);
     method public abstract void requestLayout();
+    method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public abstract void requestTransparentRegion(android.view.View);
     method public abstract boolean showContextMenuForChild(android.view.View);
     method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
@@ -22028,53 +22032,32 @@
 
 package android.view.accessibility {
 
-  public final class AccessibilityEvent implements android.os.Parcelable {
+  public final class AccessibilityEvent extends android.view.accessibility.AccessibilityRecord implements android.os.Parcelable {
+    method public void appendRecord(android.view.accessibility.AccessibilityRecord);
     method public int describeContents();
-    method public int getAddedCount();
-    method public java.lang.CharSequence getBeforeText();
-    method public java.lang.CharSequence getClassName();
-    method public java.lang.CharSequence getContentDescription();
-    method public int getCurrentItemIndex();
     method public long getEventTime();
     method public int getEventType();
-    method public int getFromIndex();
-    method public int getItemCount();
     method public java.lang.CharSequence getPackageName();
-    method public android.os.Parcelable getParcelableData();
-    method public int getRemovedCount();
-    method public java.util.List<java.lang.CharSequence> getText();
+    method public android.view.accessibility.AccessibilityRecord getRecord(int);
+    method public int getRecordCount();
     method public void initFromParcel(android.os.Parcel);
-    method public boolean isChecked();
-    method public boolean isEnabled();
-    method public boolean isFullScreen();
-    method public boolean isPassword();
     method public static android.view.accessibility.AccessibilityEvent obtain(int);
     method public static android.view.accessibility.AccessibilityEvent obtain();
-    method public void recycle();
-    method public void setAddedCount(int);
-    method public void setBeforeText(java.lang.CharSequence);
-    method public void setChecked(boolean);
-    method public void setClassName(java.lang.CharSequence);
-    method public void setContentDescription(java.lang.CharSequence);
-    method public void setCurrentItemIndex(int);
-    method public void setEnabled(boolean);
     method public void setEventTime(long);
     method public void setEventType(int);
-    method public void setFromIndex(int);
-    method public void setFullScreen(boolean);
-    method public void setItemCount(int);
     method public void setPackageName(java.lang.CharSequence);
-    method public void setParcelableData(android.os.Parcelable);
-    method public void setPassword(boolean);
-    method public void setRemovedCount(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int INVALID_POSITION = -1; // 0xffffffff
     field public static final deprecated int MAX_TEXT_LENGTH = 500; // 0x1f4
     field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
     field public static final int TYPE_NOTIFICATION_STATE_CHANGED = 64; // 0x40
+    field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+    field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
     field public static final int TYPE_VIEW_CLICKED = 1; // 0x1
     field public static final int TYPE_VIEW_FOCUSED = 8; // 0x8
+    field public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+    field public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
     field public static final int TYPE_VIEW_LONG_CLICKED = 2; // 0x2
     field public static final int TYPE_VIEW_SELECTED = 4; // 0x4
     field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10
@@ -22088,11 +22071,58 @@
 
   public final class AccessibilityManager {
     method public java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
+    method public java.util.List<android.content.pm.ServiceInfo> getEnabledAccessibilityServiceList(int);
     method public void interrupt();
     method public boolean isEnabled();
     method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
   }
 
+  public class AccessibilityRecord {
+    ctor protected AccessibilityRecord();
+    method protected void clear();
+    method public int getAddedCount();
+    method public java.lang.CharSequence getBeforeText();
+    method public boolean getBooleanProperty(int);
+    method public java.lang.CharSequence getClassName();
+    method public java.lang.CharSequence getContentDescription();
+    method public int getCurrentItemIndex();
+    method public int getFromIndex();
+    method public int getItemCount();
+    method public android.os.Parcelable getParcelableData();
+    method public int getRemovedCount();
+    method public java.util.List<java.lang.CharSequence> getText();
+    method public boolean isChecked();
+    method public boolean isEnabled();
+    method public boolean isFullScreen();
+    method public boolean isPassword();
+    method protected static android.view.accessibility.AccessibilityRecord obtain();
+    method public void recycle();
+    method public void setAddedCount(int);
+    method public void setBeforeText(java.lang.CharSequence);
+    method public void setChecked(boolean);
+    method public void setClassName(java.lang.CharSequence);
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setCurrentItemIndex(int);
+    method public void setEnabled(boolean);
+    method public void setFromIndex(int);
+    method public void setFullScreen(boolean);
+    method public void setItemCount(int);
+    method public void setParcelableData(android.os.Parcelable);
+    method public void setPassword(boolean);
+    method public void setRemovedCount(int);
+    field protected int mAddedCount;
+    field protected java.lang.CharSequence mBeforeText;
+    field protected int mBooleanProperties;
+    field protected java.lang.CharSequence mClassName;
+    field protected java.lang.CharSequence mContentDescription;
+    field protected int mCurrentItemIndex;
+    field protected int mFromIndex;
+    field protected int mItemCount;
+    field protected android.os.Parcelable mParcelableData;
+    field protected int mRemovedCount;
+    field protected final java.util.List mText;
+  }
+
 }
 
 package android.view.animation {
diff --git a/core/java/android/preference/MultiSelectListPreference.java b/core/java/android/preference/MultiSelectListPreference.java
index 42d555cf..2e8d551 100644
--- a/core/java/android/preference/MultiSelectListPreference.java
+++ b/core/java/android/preference/MultiSelectListPreference.java
@@ -169,9 +169,9 @@
                 new DialogInterface.OnMultiChoiceClickListener() {
                     public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                         if (isChecked) {
-                            mPreferenceChanged |= mNewValues.add(mEntries[which].toString());
+                            mPreferenceChanged |= mNewValues.add(mEntryValues[which].toString());
                         } else {
-                            mPreferenceChanged |= mNewValues.remove(mEntries[which].toString());
+                            mPreferenceChanged |= mNewValues.remove(mEntryValues[which].toString());
                         }
                     }
                 });
@@ -180,7 +180,7 @@
     }
     
     private boolean[] getSelectedItems() {
-        final CharSequence[] entries = mEntries;
+        final CharSequence[] entries = mEntryValues;
         final int entryCount = entries.length;
         final Set<String> values = mValues;
         boolean[] result = new boolean[entryCount];
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2f4a4bb..570b801 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3721,6 +3721,22 @@
                 "setup_prepaid_data_service_url";
 
         /**
+         * URL to attempt a GET on to see if this is a prepay device
+         * @hide
+         */
+        public static final String SETUP_PREPAID_DETECTION_TARGET_URL =
+                "setup_prepaid_detection_target_url";
+
+        /**
+         * Host to check for a redirect to after an attempt to GET
+         * SETUP_PREPAID_DETECTION_TARGET_URL. (If we redirected there,
+         * this is a prepaid device with zero balance.)
+         * @hide
+         */
+        public static final String SETUP_PREPAID_DETECTION_REDIR_HOST =
+                "setup_prepaid_detection_redir_host";
+
+        /**
          * @hide
          */
         public static final String[] SETTINGS_TO_BACKUP = {
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index 6e2168b..831ccc5 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -61,8 +61,8 @@
      * Just like {@link Paint#getTextRunAdvances}.
      * @hide
      */
-    float getTextRunAdvancesICU(int start, int end, int contextStart, int contextEnd,
-            int flags, float[] advances, int advancesIndex, Paint paint);
+    float getTextRunAdvances(int start, int end, int contextStart, int contextEnd,
+            int flags, float[] advances, int advancesIndex, Paint paint, int reserved);
 
     /**
      * Just like {@link Paint#getTextRunCursor}.
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index ff6a4cd..6b2d8e4 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1173,8 +1173,8 @@
      * Don't call this yourself -- exists for Paint to use internally.
      * {@hide}
      */
-    public float getTextRunAdvancesICU(int start, int end, int contextStart, int contextEnd, int flags,
-            float[] advances, int advancesPos, Paint p) {
+    public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, int flags,
+            float[] advances, int advancesPos, Paint p, int reserved) {
 
         float ret;
 
@@ -1182,16 +1182,16 @@
         int len = end - start;
 
         if (end <= mGapStart) {
-            ret = p.getTextRunAdvancesICU(mText, start, len, contextStart, contextLen,
-                    flags, advances, advancesPos);
+            ret = p.getTextRunAdvances(mText, start, len, contextStart, contextLen,
+                    flags, advances, advancesPos, reserved);
         } else if (start >= mGapStart) {
-            ret = p.getTextRunAdvancesICU(mText, start + mGapLength, len,
-                    contextStart + mGapLength, contextLen, flags, advances, advancesPos);
+            ret = p.getTextRunAdvances(mText, start + mGapLength, len,
+                    contextStart + mGapLength, contextLen, flags, advances, advancesPos, reserved);
         } else {
             char[] buf = TextUtils.obtain(contextLen);
             getChars(contextStart, contextEnd, buf, 0);
-            ret = p.getTextRunAdvancesICU(buf, start - contextStart, len,
-                    0, contextLen, flags, advances, advancesPos);
+            ret = p.getTextRunAdvances(buf, start - contextStart, len,
+                    0, contextLen, flags, advances, advancesPos, reserved);
             TextUtils.recycle(buf);
         }
 
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 980239b..8e839c0 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -200,8 +200,27 @@
      * @param outMetrics
      */
     public void getMetrics(DisplayMetrics outMetrics) {
-        outMetrics.widthPixels  = getWidth();
-        outMetrics.heightPixels = getHeight();
+        synchronized (mTmpPoint) {
+            getSize(mTmpPoint);
+            outMetrics.widthPixels = mTmpPoint.x;
+            outMetrics.heightPixels = mTmpPoint.y;
+        }
+        getNonSizeMetrics(outMetrics);
+    }
+
+    /**
+     * Initialize a DisplayMetrics object from this display's data.
+     *
+     * @param outMetrics
+     * @hide
+     */
+    public void getRealMetrics(DisplayMetrics outMetrics) {
+        outMetrics.widthPixels = getRealWidth();
+        outMetrics.heightPixels = getRealHeight();
+        getNonSizeMetrics(outMetrics);
+    }
+
+    private void getNonSizeMetrics(DisplayMetrics outMetrics) {
         outMetrics.density      = mDensity;
         outMetrics.densityDpi   = (int)((mDensity*DisplayMetrics.DENSITY_DEFAULT)+.5f);
         outMetrics.scaledDensity= outMetrics.density;
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
index b5ca2c2..e14b975 100644
--- a/core/java/android/view/InputEventConsistencyVerifier.java
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -30,7 +30,6 @@
  * @hide
  */
 public final class InputEventConsistencyVerifier {
-    private static final String TAG = "InputEventConsistencyVerifier";
     private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE);
 
     // The number of recent events to log when a problem is detected.
@@ -44,6 +43,11 @@
     // Consistency verifier flags.
     private final int mFlags;
 
+    // Tag for logging which a client can set to help distinguish the output
+    // from different verifiers since several can be active at the same time.
+    // If not provided defaults to the simple class name.
+    private final String mLogTag;
+
     // The most recently checked event and the nesting level at which it was checked.
     // This is only set when the verifier is called from a nesting level greater than 0
     // so that the verifier can detect when it has been asked to verify the same event twice.
@@ -103,8 +107,19 @@
      * @param flags Flags to the verifier, or 0 if none.
      */
     public InputEventConsistencyVerifier(Object caller, int flags) {
+        this(caller, flags, InputEventConsistencyVerifier.class.getSimpleName());
+    }
+
+    /**
+     * Creates an input consistency verifier.
+     * @param caller The object to which the verifier is attached.
+     * @param flags Flags to the verifier, or 0 if none.
+     * @param logTag Tag for logging. If null defaults to the short class name.
+     */
+    public InputEventConsistencyVerifier(Object caller, int flags, String logTag) {
         this.mCaller = caller;
         this.mFlags = flags;
+        this.mLogTag = (logTag != null) ? logTag : "InputEventConsistencyVerifier";
     }
 
     /**
@@ -596,7 +611,7 @@
                 }
             }
 
-            Log.d(TAG, mViolationMessage.toString());
+            Log.d(mLogTag, mViolationMessage.toString());
             mViolationMessage.setLength(0);
             tainted = true;
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4a62892..4bc7f39 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3455,6 +3455,10 @@
         if (!isShown()) {
             return;
         }
+
+        // Populate these here since they are related to the View that
+        // sends the event and should not be modified while dispatching
+        // to descendants.
         event.setClassName(getClass().getName());
         event.setPackageName(getContext().getPackageName());
         event.setEnabled(isEnabled());
@@ -3470,22 +3474,38 @@
 
         dispatchPopulateAccessibilityEvent(event);
 
-        AccessibilityManager.getInstance(mContext).sendAccessibilityEvent(event);
+        // In the beginning we called #isShown(), so we know that getParent() is not null.
+        getParent().requestSendAccessibilityEvent(this, event);
     }
 
     /**
-     * Dispatches an {@link AccessibilityEvent} to the {@link View} children
-     * to be populated.
+     * Dispatches an {@link AccessibilityEvent} to the {@link View} children to be populated.
+     * This method first calls {@link #onPopulateAccessibilityEvent(AccessibilityEvent)}
+     * on this view allowing it to populate information about itself and also decide
+     * whether to intercept the population i.e. to prevent its children from populating
+     * the event.
      *
      * @param event The event.
      *
      * @return True if the event population was completed.
      */
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        onPopulateAccessibilityEvent(event);
         return false;
     }
 
     /**
+     * Called from {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
+     * giving a chance to this View to populate the accessibility evnet with
+     * information about itself.
+     *
+     * @param event The accessibility event which to populate.
+     */
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+
+    }
+
+    /**
      * Gets the {@link View} description. It briefly describes the view and is
      * primarily used for accessibility support. Set this property to enable
      * better accessibility support for your application. This is especially
@@ -5390,20 +5410,6 @@
      * to receive the hover event.
      */
     public boolean onHoverEvent(MotionEvent event) {
-        final int viewFlags = mViewFlags;
-
-        if (((viewFlags & CLICKABLE) != CLICKABLE &&
-                (viewFlags & LONG_CLICKABLE) != LONG_CLICKABLE)) {
-            // Nothing to do if the view is not clickable.
-            return false;
-        }
-
-        if ((viewFlags & ENABLED_MASK) == DISABLED) {
-            // A disabled view that is clickable still consumes the hover events, it just doesn't
-            // respond to them.
-            return true;
-        }
-
         switch (event.getAction()) {
             case MotionEvent.ACTION_HOVER_ENTER:
                 setHovered(true);
@@ -5414,7 +5420,7 @@
                 break;
         }
 
-        return true;
+        return false;
     }
 
     /**
@@ -5436,11 +5442,13 @@
             if ((mPrivateFlags & HOVERED) == 0) {
                 mPrivateFlags |= HOVERED;
                 refreshDrawableState();
+                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
             }
         } else {
             if ((mPrivateFlags & HOVERED) != 0) {
                 mPrivateFlags &= ~HOVERED;
                 refreshDrawableState();
+                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
             }
         }
     }
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 739758c..94eb429 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -19,7 +19,6 @@
 import android.app.AppGlobals;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.os.Bundle;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.SparseArray;
@@ -156,6 +155,13 @@
     private static final int MAXIMUM_FLING_VELOCITY = 8000;
 
     /**
+     * Distance 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_EXPLORATION_TAP_SLOP = 80;
+
+    /**
      * The maximum size of View's drawing cache, expressed in bytes. This size
      * should be at least equal to the size of the screen in ARGB888 format.
      */
@@ -185,6 +191,7 @@
     private final int mTouchSlop;
     private final int mPagingTouchSlop;
     private final int mDoubleTapSlop;
+    private final int mScaledTouchExplorationTapSlop;
     private final int mWindowTouchSlop;
     private final int mMaximumDrawingCacheSize;
     private final int mOverscrollDistance;
@@ -206,6 +213,7 @@
         mTouchSlop = TOUCH_SLOP;
         mPagingTouchSlop = PAGING_TOUCH_SLOP;
         mDoubleTapSlop = DOUBLE_TAP_SLOP;
+        mScaledTouchExplorationTapSlop = TOUCH_EXPLORATION_TAP_SLOP;
         mWindowTouchSlop = WINDOW_TOUCH_SLOP;
         //noinspection deprecation
         mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
@@ -242,6 +250,7 @@
         mTouchSlop = (int) (sizeAndDensity * TOUCH_SLOP + 0.5f);
         mPagingTouchSlop = (int) (sizeAndDensity * PAGING_TOUCH_SLOP + 0.5f);
         mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f);
+        mScaledTouchExplorationTapSlop = (int) (density * TOUCH_EXPLORATION_TAP_SLOP + 0.5f);
         mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
 
         // Size of the screen in bytes, in ARGB_8888 format
@@ -444,6 +453,17 @@
     }
 
     /**
+     * @return Distance 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 getScaledTouchExplorationTapSlop() {
+        return mScaledTouchExplorationTapSlop;
+    }
+
+    /**
      * @return Distance a touch must be outside the bounds of a window for it
      * to be counted as outside the window for purposes of dismissing that
      * window.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 08daa28..7b404b4 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -586,6 +586,35 @@
     /**
      * {@inheritDoc}
      */
+    public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+        ViewParent parent = getParent();
+        if (parent == null) {
+            return false;
+        }
+        final boolean propagate = onRequestSendAccessibilityEvent(child, event);
+        if (!propagate) {
+            return false;
+        }
+        return parent.requestSendAccessibilityEvent(this, event);
+    }
+
+    /**
+     * Called when a child has requested sending an {@link AccessibilityEvent} and
+     * gives an opportunity to its parent to augment the event.
+     *
+     * @param child The child which requests sending the event.
+     * @param event The event to be sent.
+     * @return True if the event should be sent.
+     *
+     * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
+     */
+    public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public boolean dispatchUnhandledMove(View focused, int direction) {
         return mFocused != null &&
@@ -1216,9 +1245,8 @@
                 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
                 handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, mHoveredChild);
                 eventNoHistory.setAction(action);
-
                 mHoveredChild = null;
-            } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
+            } else {
                 // Pointer is still within the child.
                 handled |= dispatchTransformedGenericPointerEvent(event, mHoveredChild);
             }
@@ -1278,6 +1306,17 @@
         return handled;
     }
 
+    @Override
+    public boolean onHoverEvent(MotionEvent event) {
+        // Handle the event only if leaf. This guarantees that
+        // the leafs (or any custom class that returns true from
+        // this method) will get a change to process the hover.
+        if (getChildCount() == 0) {
+            return super.onHoverEvent(event);
+        }
+        return false;
+    }
+
     private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
         if (event.getHistorySize() == 0) {
             return event;
@@ -2091,11 +2130,16 @@
 
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        boolean populated = false;
+        // We first get a chance to populate the event.
+        onPopulateAccessibilityEvent(event);
+        // Let our children have a shot in populating the event.
         for (int i = 0, count = getChildCount(); i < count; i++) {
-            populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event);
+            boolean handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event);
+            if (handled) {
+                return handled;
+            }
         }
-        return populated;
+        return false;
     }
 
     /**
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index d7d4c3f..655df39 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.graphics.Rect;
+import android.view.accessibility.AccessibilityEvent;
 
 /**
  * Defines the responsibilities for a class that will be a parent of a View.
@@ -222,4 +223,22 @@
      */
     public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
             boolean immediate);
+
+    /**
+     * Called by a child to request from its parent to send an {@link AccessibilityEvent}.
+     * The child has already populated a record for itself in the event and is delegating
+     * to its parent to send the event. The parent can optionally add a record for itself.
+     * <p>
+     * Note: An accessibility event is fired by an individual view which populates the
+     *       event with a record for its state and requests from its parent to perform
+     *       the sending. The parent can optionally add a record for itself before
+     *       dispatching the request to its parent. A parent can also choose not to
+     *       respect the request for sending the event. The accessibility event is sent
+     *       by the topmost view in the view tree.
+     *
+     * @param child The child which requests sending the event.
+     * @param event The event to be sent.
+     * @return True if the event was sent.
+     */
+    public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event);
 }
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 4104b07..f02daba 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -3530,6 +3530,14 @@
     public void childDrawableStateChanged(View child) {
     }
 
+    public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+        if (mView == null) {
+            return false;
+        }
+        AccessibilityManager.getInstance(child.mContext).sendAccessibilityEvent(event);
+        return true;
+    }
+
     void checkThread() {
         if (mThread != Thread.currentThread()) {
             throw new CalledFromWrongThreadException(
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 8f7bb8c..3d19380 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -400,7 +400,7 @@
      * display dimensions.
      */
     public void setInitialDisplaySize(int width, int height);
-    
+
     /**
      * Check permissions when adding a window.
      * 
@@ -816,6 +816,13 @@
             boolean displayEnabled);
     
     /**
+     * Return the currently locked screen rotation, if any.  Return
+     * Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_180, or
+     * Surface.ROTATION_270 if locked; return -1 if not locked.
+     */
+    public int getLockedRotationLw();
+
+    /**
      * Called when the system is mostly done booting to determine whether
      * the system should go into safe mode.
      */
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 9af19b8..11c9392 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -21,13 +21,26 @@
 import android.text.TextUtils;
 
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * This class represents accessibility events that are sent by the system when
  * something notable happens in the user interface. For example, when a
  * {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc.
  * <p>
+ * An accessibility event is fired by an individual view which populates the event with
+ * a record for its state and requests from its parent to send the event to interested
+ * parties. The parent can optionally add a record for itself before dispatching a similar
+ * request to its parent. A parent can also choose not to respect the request for sending
+ * an event. The accessibility event is sent by the topmost view in the view tree.
+ * Therefore, an {@link android.accessibilityservice.AccessibilityService} can explore
+ * all records in an accessibility event to obtain more information about the context
+ * in which the event was fired.
+ * <p>
+ * A client can add, remove, and modify records. The getters and setters for individual
+ * properties operate on the current record which can be explicitly set by the client. By
+ * default current is the first record. Thus, querying a record would require setting
+ * it as the current one and interacting with the property getters and setters.
+ * <p>
  * This class represents various semantically different accessibility event
  * types. Each event type has associated a set of related properties. In other
  * words, each event type is characterized via a subset of the properties exposed
@@ -145,7 +158,7 @@
  * @see android.view.accessibility.AccessibilityManager
  * @see android.accessibilityservice.AccessibilityService
  */
-public final class AccessibilityEvent implements Parcelable {
+public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable {
 
     /**
      * Invalid selection/focus position.
@@ -207,6 +220,26 @@
     public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040;
 
     /**
+     * Represents the event of a hover enter over a {@link android.view.View}.
+     */
+    public static final int TYPE_VIEW_HOVER_ENTER = 0x00000080;
+
+    /**
+     * Represents the event of a hover exit over a {@link android.view.View}.
+     */
+    public static final int TYPE_VIEW_HOVER_EXIT = 0x00000100;
+
+    /**
+     * Represents the event of starting a touch exploration gesture.
+     */
+    public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200;
+
+    /**
+     * Represents the event of ending a touch exploration gesture.
+     */
+    public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400;
+
+    /**
      * Mask for {@link AccessibilityEvent} all types.
      *
      * @see #TYPE_VIEW_CLICKED
@@ -219,116 +252,53 @@
      */
     public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
 
-    private static final int MAX_POOL_SIZE = 2;
+    private static final int MAX_POOL_SIZE = 10;
     private static final Object mPoolLock = new Object();
     private static AccessibilityEvent sPool;
     private static int sPoolSize;
 
-    private static final int CHECKED = 0x00000001;
-    private static final int ENABLED = 0x00000002;
-    private static final int PASSWORD = 0x00000004;
-    private static final int FULL_SCREEN = 0x00000080;
-
     private AccessibilityEvent mNext;
+    private boolean mIsInPool;
 
     private int mEventType;
-    private int mBooleanProperties;
-    private int mCurrentItemIndex;
-    private int mItemCount;
-    private int mFromIndex;
-    private int mAddedCount;
-    private int mRemovedCount;
-
+    private CharSequence mPackageName;
     private long mEventTime;
 
-    private CharSequence mClassName;
-    private CharSequence mPackageName;
-    private CharSequence mContentDescription;
-    private CharSequence mBeforeText;
-
-    private Parcelable mParcelableData;
-
-    private final List<CharSequence> mText = new ArrayList<CharSequence>();
-
-    private boolean mIsInPool;
+    private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>();
 
     /*
      * Hide constructor from clients.
      */
     private AccessibilityEvent() {
-        mCurrentItemIndex = INVALID_POSITION;
+
     }
 
     /**
-     * Gets if the source is checked.
+     * Gets the number of records contained in the event.
      *
-     * @return True if the view is checked, false otherwise.
+     * @return The number of records.
      */
-    public boolean isChecked() {
-        return getBooleanProperty(CHECKED);
+    public int getRecordCount() {
+        return mRecords.size();
     }
 
     /**
-     * Sets if the source is checked.
+     * Appends an {@link AccessibilityRecord} to the end of event records.
      *
-     * @param isChecked True if the view is checked, false otherwise.
+     * @param record The record to append.
      */
-    public void setChecked(boolean isChecked) {
-        setBooleanProperty(CHECKED, isChecked);
+    public void appendRecord(AccessibilityRecord record) {
+        mRecords.add(record);
     }
 
     /**
-     * Gets if the source is enabled.
+     * Gets the records at a given index.
      *
-     * @return True if the view is enabled, false otherwise.
+     * @param index The index.
+     * @return The records at the specified index.
      */
-    public boolean isEnabled() {
-        return getBooleanProperty(ENABLED);
-    }
-
-    /**
-     * Sets if the source is enabled.
-     *
-     * @param isEnabled True if the view is enabled, false otherwise.
-     */
-    public void setEnabled(boolean isEnabled) {
-        setBooleanProperty(ENABLED, isEnabled);
-    }
-
-    /**
-     * Gets if the source is a password field.
-     *
-     * @return True if the view is a password field, false otherwise.
-     */
-    public boolean isPassword() {
-        return getBooleanProperty(PASSWORD);
-    }
-
-    /**
-     * Sets if the source is a password field.
-     *
-     * @param isPassword True if the view is a password field, false otherwise.
-     */
-    public void setPassword(boolean isPassword) {
-        setBooleanProperty(PASSWORD, isPassword);
-    }
-
-    /**
-     * Sets if the source is taking the entire screen.
-     *
-     * @param isFullScreen True if the source is full screen, false otherwise.
-     */
-    public void setFullScreen(boolean isFullScreen) {
-        setBooleanProperty(FULL_SCREEN, isFullScreen);
-    }
-
-    /**
-     * Gets if the source is taking the entire screen.
-     *
-     * @return True if the source is full screen, false otherwise.
-     */
-    public boolean isFullScreen() {
-        return getBooleanProperty(FULL_SCREEN);
+    public AccessibilityRecord getRecord(int index) {
+        return mRecords.get(index);
     }
 
     /**
@@ -350,96 +320,6 @@
     }
 
     /**
-     * Gets the number of items that can be visited.
-     *
-     * @return The number of items.
-     */
-    public int getItemCount() {
-        return mItemCount;
-    }
-
-    /**
-     * Sets the number of items that can be visited.
-     *
-     * @param itemCount The number of items.
-     */
-    public void setItemCount(int itemCount) {
-        mItemCount = itemCount;
-    }
-
-    /**
-     * Gets the index of the source in the list of items the can be visited.
-     *
-     * @return The current item index.
-     */
-    public int getCurrentItemIndex() {
-        return mCurrentItemIndex;
-    }
-
-    /**
-     * Sets the index of the source in the list of items that can be visited.
-     *
-     * @param currentItemIndex The current item index.
-     */
-    public void setCurrentItemIndex(int currentItemIndex) {
-        mCurrentItemIndex = currentItemIndex;
-    }
-
-    /**
-     * Gets the index of the first character of the changed sequence.
-     *
-     * @return The index of the first character.
-     */
-    public int getFromIndex() {
-        return mFromIndex;
-    }
-
-    /**
-     * Sets the index of the first character of the changed sequence.
-     *
-     * @param fromIndex The index of the first character.
-     */
-    public void setFromIndex(int fromIndex) {
-        mFromIndex = fromIndex;
-    }
-
-    /**
-     * Gets the number of added characters.
-     *
-     * @return The number of added characters.
-     */
-    public int getAddedCount() {
-        return mAddedCount;
-    }
-
-    /**
-     * Sets the number of added characters.
-     *
-     * @param addedCount The number of added characters.
-     */
-    public void setAddedCount(int addedCount) {
-        mAddedCount = addedCount;
-    }
-
-    /**
-     * Gets the number of removed characters.
-     *
-     * @return The number of removed characters.
-     */
-    public int getRemovedCount() {
-        return mRemovedCount;
-    }
-
-    /**
-     * Sets the number of removed characters.
-     *
-     * @param removedCount The number of removed characters.
-     */
-    public void setRemovedCount(int removedCount) {
-        mRemovedCount = removedCount;
-    }
-
-    /**
      * Gets the time in which this event was sent.
      *
      * @return The event time.
@@ -458,24 +338,6 @@
     }
 
     /**
-     * Gets the class name of the source.
-     *
-     * @return The class name.
-     */
-    public CharSequence getClassName() {
-        return mClassName;
-    }
-
-    /**
-     * Sets the class name of the source.
-     *
-     * @param className The lass name.
-     */
-    public void setClassName(CharSequence className) {
-        mClassName = className;
-    }
-
-    /**
      * Gets the package name of the source.
      *
      * @return The package name.
@@ -494,70 +356,6 @@
     }
 
     /**
-     * Gets the text of the event. The index in the list represents the priority
-     * of the text. Specifically, the lower the index the higher the priority.
-     *
-     * @return The text.
-     */
-    public List<CharSequence> getText() {
-        return mText;
-    }
-
-    /**
-     * Sets the text before a change.
-     *
-     * @return The text before the change.
-     */
-    public CharSequence getBeforeText() {
-        return mBeforeText;
-    }
-
-    /**
-     * Sets the text before a change.
-     *
-     * @param beforeText The text before the change.
-     */
-    public void setBeforeText(CharSequence beforeText) {
-        mBeforeText = beforeText;
-    }
-
-    /**
-     * Gets the description of the source.
-     *
-     * @return The description.
-     */
-    public CharSequence getContentDescription() {
-        return mContentDescription;
-    }
-
-    /**
-     * Sets the description of the source.
-     *
-     * @param contentDescription The description.
-     */
-    public void setContentDescription(CharSequence contentDescription) {
-        mContentDescription = contentDescription;
-    }
-
-    /**
-     * Gets the {@link Parcelable} data.
-     *
-     * @return The parcelable data.
-     */
-    public Parcelable getParcelableData() {
-        return mParcelableData;
-    }
-
-    /**
-     * Sets the {@link Parcelable} data of the event.
-     *
-     * @param parcelableData The parcelable data.
-     */
-    public void setParcelableData(Parcelable parcelableData) {
-        mParcelableData = parcelableData;
-    }
-
-    /**
      * Returns a cached instance if such is available or a new one is
      * instantiated with type property set.
      *
@@ -595,11 +393,11 @@
      * <p>
      * <b>Note: You must not touch the object after calling this function.</b>
      */
+    @Override
     public void recycle() {
         if (mIsInPool) {
             return;
         }
-
         clear();
         synchronized (mPoolLock) {
             if (sPoolSize <= MAX_POOL_SIZE) {
@@ -614,44 +412,15 @@
     /**
      * Clears the state of this instance.
      */
-    private void clear() {
+    @Override
+    protected void clear() {
+        super.clear();
         mEventType = 0;
-        mBooleanProperties = 0;
-        mCurrentItemIndex = INVALID_POSITION;
-        mItemCount = 0;
-        mFromIndex = 0;
-        mAddedCount = 0;
-        mRemovedCount = 0;
-        mEventTime = 0;
-        mClassName = null;
         mPackageName = null;
-        mContentDescription = null;
-        mBeforeText = null;
-        mParcelableData = null;
-        mText.clear();
-    }
-
-    /**
-     * Gets the value of a boolean property.
-     *
-     * @param property The property.
-     * @return The value.
-     */
-    private boolean getBooleanProperty(int property) {
-        return (mBooleanProperties & property) == property;
-    }
-
-    /**
-     * Sets a boolean property.
-     *
-     * @param property The property.
-     * @param value The value.
-     */
-    private void setBooleanProperty(int property, boolean value) {
-        if (value) {
-            mBooleanProperties |= property;
-        } else {
-            mBooleanProperties &= ~property;
+        mEventTime = 0;
+        while (!mRecords.isEmpty()) {
+            AccessibilityRecord record = mRecords.remove(0);
+            record.recycle();
         }
     }
 
@@ -662,38 +431,82 @@
      */
     public void initFromParcel(Parcel parcel) {
         mEventType = parcel.readInt();
-        mBooleanProperties = parcel.readInt();
-        mCurrentItemIndex = parcel.readInt();
-        mItemCount = parcel.readInt();
-        mFromIndex = parcel.readInt();
-        mAddedCount = parcel.readInt();
-        mRemovedCount = parcel.readInt();
-        mEventTime = parcel.readLong();
-        mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
         mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
-        mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
-        mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
-        mParcelableData = parcel.readParcelable(null);
-        parcel.readList(mText, null);
+        mEventTime = parcel.readLong();
+        readAccessibilityRecordFromParcel(this, parcel);
+
+        // Read the records.
+        final int recordCount = parcel.readInt();
+        for (int i = 0; i < recordCount; i++) {
+            AccessibilityRecord record = AccessibilityRecord.obtain();
+            readAccessibilityRecordFromParcel(record, parcel);
+            mRecords.add(record);
+        }
     }
 
+    /**
+     * Reads an {@link AccessibilityRecord} from a parcel.
+     *
+     * @param record The record to initialize.
+     * @param parcel The parcel to read from.
+     */
+    private void readAccessibilityRecordFromParcel(AccessibilityRecord record,
+            Parcel parcel) {
+        record.mBooleanProperties = parcel.readInt();
+        record.mCurrentItemIndex = parcel.readInt();
+        record.mItemCount = parcel.readInt();
+        record.mFromIndex = parcel.readInt();
+        record.mAddedCount = parcel.readInt();
+        record.mRemovedCount = parcel.readInt();
+        record.mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        record.mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        record.mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        record.mParcelableData = parcel.readParcelable(null);
+        parcel.readList(record.mText, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mEventType);
-        parcel.writeInt(mBooleanProperties);
-        parcel.writeInt(mCurrentItemIndex);
-        parcel.writeInt(mItemCount);
-        parcel.writeInt(mFromIndex);
-        parcel.writeInt(mAddedCount);
-        parcel.writeInt(mRemovedCount);
-        parcel.writeLong(mEventTime);
-        TextUtils.writeToParcel(mClassName, parcel, 0);
         TextUtils.writeToParcel(mPackageName, parcel, 0);
-        TextUtils.writeToParcel(mContentDescription, parcel, 0);
-        TextUtils.writeToParcel(mBeforeText, parcel, 0);
-        parcel.writeParcelable(mParcelableData, flags);
-        parcel.writeList(mText);
+        parcel.writeLong(mEventTime);
+        writeAccessibilityRecordToParcel(this, parcel, flags);
+
+        // Write the records.
+        final int recordCount = getRecordCount();
+        parcel.writeInt(recordCount);
+        for (int i = 0; i < recordCount; i++) {
+            AccessibilityRecord record = mRecords.get(i);
+            writeAccessibilityRecordToParcel(record, parcel, flags);
+        }
     }
 
+    /**
+     * Writes an {@link AccessibilityRecord} to a parcel.
+     *
+     * @param record The record to write.
+     * @param parcel The parcel to which to write.
+     */
+    private void writeAccessibilityRecordToParcel(AccessibilityRecord record, Parcel parcel,
+            int flags) {
+        parcel.writeInt(record.mBooleanProperties);
+        parcel.writeInt(record.mCurrentItemIndex);
+        parcel.writeInt(record.mItemCount);
+        parcel.writeInt(record.mFromIndex);
+        parcel.writeInt(record.mAddedCount);
+        parcel.writeInt(record.mRemovedCount);
+        TextUtils.writeToParcel(record.mClassName, parcel, flags);
+        TextUtils.writeToParcel(record.mContentDescription, parcel, flags);
+        TextUtils.writeToParcel(record.mBeforeText, parcel, flags);
+        parcel.writeParcelable(record.mParcelableData, flags);
+        parcel.writeList(record.mText);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public int describeContents() {
         return 0;
     }
@@ -701,24 +514,21 @@
     @Override
     public String toString() {
         StringBuilder builder = new StringBuilder();
-        builder.append(super.toString());
         builder.append("; EventType: " + mEventType);
         builder.append("; EventTime: " + mEventTime);
-        builder.append("; ClassName: " + mClassName);
         builder.append("; PackageName: " + mPackageName);
-        builder.append("; Text: " + mText);
-        builder.append("; ContentDescription: " + mContentDescription);
-        builder.append("; ItemCount: " + mItemCount);
-        builder.append("; CurrentItemIndex: " + mCurrentItemIndex);
-        builder.append("; IsEnabled: " + isEnabled());
-        builder.append("; IsPassword: " + isPassword());
-        builder.append("; IsChecked: " + isChecked());
-        builder.append("; IsFullScreen: " + isFullScreen());
-        builder.append("; BeforeText: " + mBeforeText);
-        builder.append("; FromIndex: " + mFromIndex);
-        builder.append("; AddedCount: " + mAddedCount);
-        builder.append("; RemovedCount: " + mRemovedCount);
-        builder.append("; ParcelableData: " + mParcelableData);
+        builder.append(" \n{\n");
+        builder.append(super.toString());
+        builder.append("\n");
+        for (int i = 0; i < mRecords.size(); i++) {
+            AccessibilityRecord record = mRecords.get(i);
+            builder.append("  Record ");
+            builder.append(i);
+            builder.append(":");
+            builder.append(record.toString());
+            builder.append("\n");
+        }
+        builder.append("}\n");
         return builder.toString();
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 22cb0d4..dd77193 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -16,6 +16,8 @@
 
 package android.view.accessibility;
 
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
 import android.content.Context;
 import android.content.pm.ServiceInfo;
 import android.os.Binder;
@@ -44,6 +46,8 @@
  * @see android.content.Context#getSystemService
  */
 public final class AccessibilityManager {
+    private static final boolean DEBUG = false;
+
     private static final String LOG_TAG = "AccessibilityManager";
 
     static final Object sInstanceSync = new Object();
@@ -164,7 +168,7 @@
             long identityToken = Binder.clearCallingIdentity();
             doRecycle = mService.sendAccessibilityEvent(event);
             Binder.restoreCallingIdentity(identityToken);
-            if (false) {
+            if (DEBUG) {
                 Log.i(LOG_TAG, event + " sent");
             }
         } catch (RemoteException re) {
@@ -185,7 +189,7 @@
         }
         try {
             mService.interrupt();
-            if (false) {
+            if (DEBUG) {
                 Log.i(LOG_TAG, "Requested interrupt from all services");
             }
         } catch (RemoteException re) {
@@ -202,7 +206,33 @@
         List<ServiceInfo> services = null;
         try {
             services = mService.getAccessibilityServiceList();
-            if (false) {
+            if (DEBUG) {
+                Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
+        }
+        return Collections.unmodifiableList(services);
+    }
+
+    /**
+     * Returns the {@link ServiceInfo}s of the enabled accessibility services
+     * for a given feedback type.
+     *
+     * @param feedbackType The type of feedback.
+     * @return An unmodifiable list with {@link ServiceInfo}s.
+     *
+     * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
+     * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
+     * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
+     * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
+     * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
+     */
+    public List<ServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
+        List<ServiceInfo> services = null;
+        try {
+            services = mService.getEnabledAccessibilityServiceList(feedbackType);
+            if (DEBUG) {
                 Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
             }
         } catch (RemoteException re) {
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
new file mode 100644
index 0000000..e095f43
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a record in an accessibility event. This class encapsulates
+ * the information for a {@link android.view.View}. Note that not all properties
+ * are applicable to all view types. For detailed information please refer to
+ * {@link AccessibilityEvent}.
+ *
+ * @see AccessibilityEvent
+ */
+public class AccessibilityRecord {
+
+    private static final int INVALID_POSITION = -1;
+
+    private static final int PROPERTY_CHECKED = 0x00000001;
+    private static final int PROPERTY_ENABLED = 0x00000002;
+    private static final int PROPERTY_PASSWORD = 0x00000004;
+    private static final int PROPERTY_FULL_SCREEN = 0x00000080;
+
+    private static final int MAX_POOL_SIZE = 10;
+    private static final Object mPoolLock = new Object();
+    private static AccessibilityRecord sPool;
+    private static int sPoolSize;
+
+    private AccessibilityRecord mNext;
+    private boolean mIsInPool;
+
+    protected int mBooleanProperties;
+    protected int mCurrentItemIndex;
+    protected int mItemCount;
+    protected int mFromIndex;
+    protected int mAddedCount;
+    protected int mRemovedCount;
+
+    protected CharSequence mClassName;
+    protected CharSequence mContentDescription;
+    protected CharSequence mBeforeText;
+    protected Parcelable mParcelableData;
+
+    protected final List<CharSequence> mText = new ArrayList<CharSequence>();
+
+    /*
+     * Hide constructor.
+     */
+    protected AccessibilityRecord() {
+
+    }
+
+    /**
+     * Gets if the source is checked.
+     *
+     * @return True if the view is checked, false otherwise.
+     */
+    public boolean isChecked() {
+        return getBooleanProperty(PROPERTY_CHECKED);
+    }
+
+    /**
+     * Sets if the source is checked.
+     *
+     * @param isChecked True if the view is checked, false otherwise.
+     */
+    public void setChecked(boolean isChecked) {
+        setBooleanProperty(PROPERTY_CHECKED, isChecked);
+    }
+
+    /**
+     * Gets if the source is enabled.
+     *
+     * @return True if the view is enabled, false otherwise.
+     */
+    public boolean isEnabled() {
+        return getBooleanProperty(PROPERTY_ENABLED);
+    }
+
+    /**
+     * Sets if the source is enabled.
+     *
+     * @param isEnabled True if the view is enabled, false otherwise.
+     */
+    public void setEnabled(boolean isEnabled) {
+        setBooleanProperty(PROPERTY_ENABLED, isEnabled);
+    }
+
+    /**
+     * Gets if the source is a password field.
+     *
+     * @return True if the view is a password field, false otherwise.
+     */
+    public boolean isPassword() {
+        return getBooleanProperty(PROPERTY_PASSWORD);
+    }
+
+    /**
+     * Sets if the source is a password field.
+     *
+     * @param isPassword True if the view is a password field, false otherwise.
+     */
+    public void setPassword(boolean isPassword) {
+        setBooleanProperty(PROPERTY_PASSWORD, isPassword);
+    }
+
+    /**
+     * Sets if the source is taking the entire screen.
+     *
+     * @param isFullScreen True if the source is full screen, false otherwise.
+     */
+    public void setFullScreen(boolean isFullScreen) {
+        setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen);
+    }
+
+    /**
+     * Gets if the source is taking the entire screen.
+     *
+     * @return True if the source is full screen, false otherwise.
+     */
+    public boolean isFullScreen() {
+        return getBooleanProperty(PROPERTY_FULL_SCREEN);
+    }
+
+    /**
+     * Gets the number of items that can be visited.
+     *
+     * @return The number of items.
+     */
+    public int getItemCount() {
+        return mItemCount;
+    }
+
+    /**
+     * Sets the number of items that can be visited.
+     *
+     * @param itemCount The number of items.
+     */
+    public void setItemCount(int itemCount) {
+        mItemCount = itemCount;
+    }
+
+    /**
+     * Gets the index of the source in the list of items the can be visited.
+     *
+     * @return The current item index.
+     */
+    public int getCurrentItemIndex() {
+        return mCurrentItemIndex;
+    }
+
+    /**
+     * Sets the index of the source in the list of items that can be visited.
+     *
+     * @param currentItemIndex The current item index.
+     */
+    public void setCurrentItemIndex(int currentItemIndex) {
+        mCurrentItemIndex = currentItemIndex;
+    }
+
+    /**
+     * Gets the index of the first character of the changed sequence.
+     *
+     * @return The index of the first character.
+     */
+    public int getFromIndex() {
+        return mFromIndex;
+    }
+
+    /**
+     * Sets the index of the first character of the changed sequence.
+     *
+     * @param fromIndex The index of the first character.
+     */
+    public void setFromIndex(int fromIndex) {
+        mFromIndex = fromIndex;
+    }
+
+    /**
+     * Gets the number of added characters.
+     *
+     * @return The number of added characters.
+     */
+    public int getAddedCount() {
+        return mAddedCount;
+    }
+
+    /**
+     * Sets the number of added characters.
+     *
+     * @param addedCount The number of added characters.
+     */
+    public void setAddedCount(int addedCount) {
+        mAddedCount = addedCount;
+    }
+
+    /**
+     * Gets the number of removed characters.
+     *
+     * @return The number of removed characters.
+     */
+    public int getRemovedCount() {
+        return mRemovedCount;
+    }
+
+    /**
+     * Sets the number of removed characters.
+     *
+     * @param removedCount The number of removed characters.
+     */
+    public void setRemovedCount(int removedCount) {
+        mRemovedCount = removedCount;
+    }
+
+    /**
+     * Gets the class name of the source.
+     *
+     * @return The class name.
+     */
+    public CharSequence getClassName() {
+        return mClassName;
+    }
+
+    /**
+     * Sets the class name of the source.
+     *
+     * @param className The lass name.
+     */
+    public void setClassName(CharSequence className) {
+        mClassName = className;
+    }
+
+    /**
+     * Gets the text of the event. The index in the list represents the priority
+     * of the text. Specifically, the lower the index the higher the priority.
+     *
+     * @return The text.
+     */
+    public List<CharSequence> getText() {
+        return mText;
+    }
+
+    /**
+     * Sets the text before a change.
+     *
+     * @return The text before the change.
+     */
+    public CharSequence getBeforeText() {
+        return mBeforeText;
+    }
+
+    /**
+     * Sets the text before a change.
+     *
+     * @param beforeText The text before the change.
+     */
+    public void setBeforeText(CharSequence beforeText) {
+        mBeforeText = beforeText;
+    }
+
+    /**
+     * Gets the description of the source.
+     *
+     * @return The description.
+     */
+    public CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    /**
+     * Sets the description of the source.
+     *
+     * @param contentDescription The description.
+     */
+    public void setContentDescription(CharSequence contentDescription) {
+        mContentDescription = contentDescription;
+    }
+
+    /**
+     * Gets the {@link Parcelable} data.
+     *
+     * @return The parcelable data.
+     */
+    public Parcelable getParcelableData() {
+        return mParcelableData;
+    }
+
+    /**
+     * Sets the {@link Parcelable} data of the event.
+     *
+     * @param parcelableData The parcelable data.
+     */
+    public void setParcelableData(Parcelable parcelableData) {
+        mParcelableData = parcelableData;
+    }
+
+    /**
+     * Gets the value of a boolean property.
+     *
+     * @param property The property.
+     * @return The value.
+     */
+    public boolean getBooleanProperty(int property) {
+        return (mBooleanProperties & property) == property;
+    }
+
+    /**
+     * Sets a boolean property.
+     *
+     * @param property The property.
+     * @param value The value.
+     */
+    private void setBooleanProperty(int property, boolean value) {
+        if (value) {
+            mBooleanProperties |= property;
+        } else {
+            mBooleanProperties &= ~property;
+        }
+    }
+
+    /**
+     * Returns a cached instance if such is available or a new one is
+     * instantiated.
+     *
+     * @return An instance.
+     */
+    protected static AccessibilityRecord obtain() {
+        synchronized (mPoolLock) {
+            if (sPool != null) {
+                AccessibilityRecord record = sPool;
+                sPool = sPool.mNext;
+                sPoolSize--;
+                record.mNext = null;
+                record.mIsInPool = false;
+                return record;
+            }
+            return new AccessibilityRecord();
+        }
+    }
+
+    /**
+     * Return an instance back to be reused.
+     * <p>
+     * <b>Note: You must not touch the object after calling this function.</b>
+     */
+    public void recycle() {
+        if (mIsInPool) {
+            return;
+        }
+        clear();
+        synchronized (mPoolLock) {
+            if (sPoolSize <= MAX_POOL_SIZE) {
+                mNext = sPool;
+                sPool = this;
+                mIsInPool = true;
+                sPoolSize++;
+            }
+        }
+    }
+
+    /**
+     * Clears the state of this instance.
+     */
+    protected void clear() {
+        mBooleanProperties = 0;
+        mCurrentItemIndex = INVALID_POSITION;
+        mItemCount = 0;
+        mFromIndex = 0;
+        mAddedCount = 0;
+        mRemovedCount = 0;
+        mClassName = null;
+        mContentDescription = null;
+        mBeforeText = null;
+        mParcelableData = null;
+        mText.clear();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(" [ ClassName: " + mClassName);
+        builder.append("; Text: " + mText);
+        builder.append("; ContentDescription: " + mContentDescription);
+        builder.append("; ItemCount: " + mItemCount);
+        builder.append("; CurrentItemIndex: " + mCurrentItemIndex);
+        builder.append("; IsEnabled: " + getBooleanProperty(PROPERTY_ENABLED));
+        builder.append("; IsPassword: " + getBooleanProperty(PROPERTY_PASSWORD));
+        builder.append("; IsChecked: " + getBooleanProperty(PROPERTY_CHECKED));
+        builder.append("; IsFullScreen: " + getBooleanProperty(PROPERTY_FULL_SCREEN));
+        builder.append("; BeforeText: " + mBeforeText);
+        builder.append("; FromIndex: " + mFromIndex);
+        builder.append("; AddedCount: " + mAddedCount);
+        builder.append("; RemovedCount: " + mRemovedCount);
+        builder.append("; ParcelableData: " + mParcelableData);
+        builder.append(" ]");
+        return builder.toString();
+    }
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 7633569d..aaaae32 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -35,5 +35,7 @@
 
     List<ServiceInfo> getAccessibilityServiceList();
 
+    List<ServiceInfo> getEnabledAccessibilityServiceList(int feedbackType);
+
     void interrupt();
 }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 6cb5c35..d63d421 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -55,6 +55,7 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -2556,6 +2557,17 @@
     }
 
     @Override
+    public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+        // Add a record for ourselves as well.
+        AccessibilityEvent record = AccessibilityEvent.obtain();
+        // Set the class since it is not populated in #dispatchPopulateAccessibilityEvent
+        record.setClassName(getClass().getName());
+        child.dispatchPopulateAccessibilityEvent(record);
+        event.appendRecord(record);
+        return true;
+    }
+
+    @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         return false;
     }
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index f16efbd..060f1a9 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -876,7 +876,6 @@
 
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        boolean populated = false;
         // This is an exceptional case which occurs when a window gets the
         // focus and sends a focus event via its focused child to announce
         // current focus/selection. AdapterView fires selection but not focus
@@ -885,22 +884,27 @@
             event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED);
         }
 
-        // we send selection events only from AdapterView to avoid
-        // generation of such event for each child
+        // We first get a chance to populate the event.
+        onPopulateAccessibilityEvent(event);
+
+        // We send selection events only from AdapterView to avoid
+        // generation of such event for each child.
         View selectedView = getSelectedView();
         if (selectedView != null) {
-            populated = selectedView.dispatchPopulateAccessibilityEvent(event);
+            return selectedView.dispatchPopulateAccessibilityEvent(event);
         }
 
-        if (!populated) {
-            if (selectedView != null) {
-                event.setEnabled(selectedView.isEnabled());
-            }
-            event.setItemCount(getCount());
-            event.setCurrentItemIndex(getSelectedItemPosition());
-        }
+        return false;
+    }
 
-        return populated;
+    @Override
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        View selectedView = getSelectedView();
+        if (selectedView != null) {
+            event.setEnabled(selectedView.isEnabled());
+        }
+        event.setItemCount(getCount());
+        event.setCurrentItemIndex(getSelectedItemPosition());
     }
 
     @Override
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index bf63607..bd595a5 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -199,11 +199,8 @@
     }
 
     @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        boolean populated = super.dispatchPopulateAccessibilityEvent(event);
-        if (!populated) {
-            event.setChecked(mChecked);
-        }
-        return populated;
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+        event.setChecked(mChecked);
     }
 }
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 0df45cc..f050d41 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -208,22 +208,18 @@
     }
 
     @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        boolean populated = super.dispatchPopulateAccessibilityEvent(event);
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
 
-        if (!populated) {
-            int resourceId = 0;
-            if (mChecked) {
-                resourceId = R.string.accessibility_compound_button_selected;
-            } else {
-                resourceId = R.string.accessibility_compound_button_unselected;
-            }
-            String state = getResources().getString(resourceId);
-            event.getText().add(state);
-            event.setChecked(mChecked);
+        int resourceId = 0;
+        if (mChecked) {
+            resourceId = R.string.accessibility_compound_button_selected;
+        } else {
+            resourceId = R.string.accessibility_compound_button_unselected;
         }
-
-        return populated;
+        String state = getResources().getString(resourceId);
+        event.getText().add(state);
+        event.setChecked(mChecked);
     }
 
     @Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 7210e21..30fb927 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -353,13 +353,14 @@
     }
 
     @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+
+        final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY
                 | DateUtils.FORMAT_SHOW_YEAR;
         String selectedDateUtterance = DateUtils.formatDateTime(mContext,
                 mCurrentDate.getTimeInMillis(), flags);
         event.getText().add(selectedDateUtterance);
-        return true;
     }
 
     /**
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index d76a956..5618dbe 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1998,36 +1998,32 @@
     }
 
     @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        boolean populated = super.dispatchPopulateAccessibilityEvent(event);
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
 
         // If the item count is less than 15 then subtract disabled items from the count and
         // position. Otherwise ignore disabled items.
-        if (!populated) {
-            int itemCount = 0;
-            int currentItemIndex = getSelectedItemPosition();
+        int itemCount = 0;
+        int currentItemIndex = getSelectedItemPosition();
 
-            ListAdapter adapter = getAdapter();
-            if (adapter != null) {
-                final int count = adapter.getCount();
-                if (count < 15) {
-                    for (int i = 0; i < count; i++) {
-                        if (adapter.isEnabled(i)) {
-                            itemCount++;
-                        } else if (i <= currentItemIndex) {
-                            currentItemIndex--;
-                        }
+        ListAdapter adapter = getAdapter();
+        if (adapter != null) {
+            final int count = adapter.getCount();
+            if (count < 15) {
+                for (int i = 0; i < count; i++) {
+                    if (adapter.isEnabled(i)) {
+                        itemCount++;
+                    } else if (i <= currentItemIndex) {
+                        currentItemIndex--;
                     }
-                } else {
-                    itemCount = count;
                 }
+            } else {
+                itemCount = count;
             }
-
-            event.setItemCount(itemCount);
-            event.setCurrentItemIndex(currentItemIndex);
         }
 
-        return populated;
+        event.setItemCount(itemCount);
+        event.setCurrentItemIndex(currentItemIndex);
     }
 
     /**
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 8db34d9..96d41a0 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1027,12 +1027,10 @@
     }
 
     @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        if (!super.dispatchPopulateAccessibilityEvent(event)) {
-            event.setItemCount(mMax);
-            event.setCurrentItemIndex(mProgress);
-        }
-        return true;
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+        event.setItemCount(mMax);
+        event.setCurrentItemIndex(mProgress);
     }
 
     /**
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 6f76dd0..31ec785 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -427,12 +427,19 @@
 
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        onPopulateAccessibilityEvent(event);
+        // Dispatch only to the selected tab.
+        if (mSelectedTab != -1) {
+            return getChildTabViewAt(mSelectedTab).dispatchPopulateAccessibilityEvent(event);
+        }
+        return false;
+    }
+
+    @Override
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
         event.setItemCount(getTabCount());
         event.setCurrentItemIndex(mSelectedTab);
-        if (mSelectedTab != -1) {
-            getChildTabViewAt(mSelectedTab).dispatchPopulateAccessibilityEvent(event);
-        }
-        return true;
     }
 
     /**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 537709d..4d3aa68 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -81,8 +81,8 @@
 import android.text.method.TimeKeyListener;
 import android.text.method.TransformationMethod;
 import android.text.style.ClickableSpan;
-import android.text.style.SuggestionSpan;
 import android.text.style.ParagraphStyle;
+import android.text.style.SuggestionSpan;
 import android.text.style.URLSpan;
 import android.text.style.UpdateAppearance;
 import android.text.util.Linkify;
@@ -2967,14 +2967,14 @@
                     advancesIndex);
         }
 
-        public float getTextRunAdvancesICU(int start, int end, int contextStart,
+        public float getTextRunAdvances(int start, int end, int contextStart,
                 int contextEnd, int flags, float[] advances, int advancesIndex,
-                Paint p) {
+                Paint p, int reserved) {
             int count = end - start;
             int contextCount = contextEnd - contextStart;
-            return p.getTextRunAdvancesICU(mChars, start + mStart, count,
+            return p.getTextRunAdvances(mChars, start + mStart, count,
                     contextStart + mStart, contextCount, flags, advances,
-                    advancesIndex);
+                    advancesIndex, reserved);
         }
 
         public int getTextRunCursor(int contextStart, int contextEnd, int flags,
@@ -7896,9 +7896,9 @@
     }
 
     @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
         if (!isShown()) {
-            return false;
+            return;
         }
 
         final boolean isPassword = hasPasswordTransformationMethod();
@@ -7914,7 +7914,6 @@
         } else {
             event.setPassword(isPassword);
         }
-        return false;
     }
 
     void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
@@ -8038,7 +8037,7 @@
             case ID_SELECTION_MODE:
                 if (mSelectionActionMode != null) {
                     // Selection mode is already started, simply change selected part.
-                    updateSelectedRegion();
+                    selectCurrentWord();
                 } else {
                     startSelectionActionMode();
                 }
@@ -8188,8 +8187,7 @@
                 startDrag(data, getTextThumbnailBuilder(selectedText), localState, 0);
                 stopSelectionActionMode();
             } else {
-                // New selection at touch position
-                updateSelectedRegion();
+                selectCurrentWord();
             }
             handled = true;
         }
@@ -8205,17 +8203,6 @@
         return handled;
     }
 
-    /**
-     * When selection mode is already started, this method simply updates the selected part of text
-     * to the text under the finger.
-     */
-    private void updateSelectedRegion() {
-        // Start a new selection at current position, keep selectionAction mode on
-        selectCurrentWord();
-        // Updates handles' positions
-        getSelectionController().show();
-    }
-
     private boolean touchPositionIsInSelection() {
         int selectionStart = getSelectionStart();
         int selectionEnd = getSelectionEnd();
@@ -8783,7 +8770,7 @@
         private float mTouchOffsetY;
         // Where the touch position should be on the handle to ensure a maximum cursor visibility
         private float mIdealVerticalOffset;
-        // Parent's (TextView) position in window
+        // Parent's (TextView) previous position in window
         private int mLastParentX, mLastParentY;
         // PopupWindow container absolute position with respect to the enclosing window
         private int mContainerPositionX, mContainerPositionY;
@@ -8857,12 +8844,9 @@
         }
 
         public void show() {
-            updateContainerPosition();
             if (isShowing()) {
                 mContainer.update(mContainerPositionX, mContainerPositionY,
                         mRight - mLeft, mBottom - mTop);
-
-                hideAssociatedPopupWindow();
             } else {
                 mContainer.showAtLocation(TextView.this, 0,
                         mContainerPositionX, mContainerPositionY);
@@ -8877,7 +8861,6 @@
         protected void dismiss() {
             mIsDragging = false;
             mContainer.dismiss();
-            hideAssociatedPopupWindow();
         }
 
         public void hide() {
@@ -8908,22 +8891,22 @@
             final int compoundPaddingLeft = getCompoundPaddingLeft();
             final int compoundPaddingRight = getCompoundPaddingRight();
 
-            final TextView hostView = TextView.this;
+            final TextView textView = TextView.this;
 
             if (mTempRect == null) mTempRect = new Rect();
             final Rect clip = mTempRect;
             clip.left = compoundPaddingLeft;
             clip.top = extendedPaddingTop;
-            clip.right = hostView.getWidth() - compoundPaddingRight;
-            clip.bottom = hostView.getHeight() - extendedPaddingBottom;
+            clip.right = textView.getWidth() - compoundPaddingRight;
+            clip.bottom = textView.getHeight() - extendedPaddingBottom;
 
-            final ViewParent parent = hostView.getParent();
-            if (parent == null || !parent.getChildVisibleRect(hostView, clip, null)) {
+            final ViewParent parent = textView.getParent();
+            if (parent == null || !parent.getChildVisibleRect(textView, clip, null)) {
                 return false;
             }
 
             final int[] coords = mTempCoords;
-            hostView.getLocationInWindow(coords);
+            textView.getLocationInWindow(coords);
             final int posX = coords[0] + mPositionX + (int) mHotspotX;
             final int posY = coords[1] + mPositionY;
 
@@ -8932,23 +8915,6 @@
                     posY >= clip.top && posY <= clip.bottom;
         }
 
-        private void moveTo(int x, int y) {
-            mPositionX = x - TextView.this.mScrollX;
-            mPositionY = y - TextView.this.mScrollY;
-
-            if (mIsDragging) {
-                TextView.this.getLocationInWindow(mTempCoords);
-                if (mTempCoords[0] != mLastParentX || mTempCoords[1] != mLastParentY) {
-                    mTouchToWindowOffsetX += mTempCoords[0] - mLastParentX;
-                    mTouchToWindowOffsetY += mTempCoords[1] - mLastParentY;
-                    mLastParentX = mTempCoords[0];
-                    mLastParentY = mTempCoords[1];
-                }
-
-                hideAssociatedPopupWindow();
-            }
-        }
-
         public abstract int getCurrentCursorOffset();
 
         public abstract void updateOffset(int offset);
@@ -8957,44 +8923,44 @@
 
         protected void positionAtCursorOffset(int offset) {
             addPositionToTouchUpFilter(offset);
-            final int width = mDrawable.getIntrinsicWidth();
-            final int height = mDrawable.getIntrinsicHeight();
             final int line = mLayout.getLineForOffset(offset);
             final int lineBottom = mLayout.getLineBottom(line);
 
-            final Rect bounds = sCursorControllerTempRect;
-            bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX) +
-                    TextView.this.mScrollX;
-            bounds.top = lineBottom + TextView.this.mScrollY;
+            mPositionX = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX);
+            mPositionY = lineBottom;
 
-            bounds.right = bounds.left + width;
-            bounds.bottom = bounds.top + height;
-
-            convertFromViewportToContentCoordinates(bounds);
-            moveTo(bounds.left, bounds.top);
+            // Take TextView's padding into account.
+            mPositionX += viewportToContentHorizontalOffset();
+            mPositionY += viewportToContentVerticalOffset();
         }
 
-        /**
-         * Updates the global container's position.
-         * @return whether or not the position has actually changed
-         */
-        private boolean updateContainerPosition() {
+        protected boolean updateContainerPosition() {
             positionAtCursorOffset(getCurrentCursorOffset());
-            TextView.this.getLocationInWindow(mTempCoords);
-            final int containerPositionX = mTempCoords[0] + mPositionX;
-            final int containerPositionY = mTempCoords[1] + mPositionY;
 
-            if (containerPositionX != mContainerPositionX ||
-                containerPositionY != mContainerPositionY) {
-                mContainerPositionX = containerPositionX;
-                mContainerPositionY = containerPositionY;
-                return true;
-            }
-            return false;
+            final int previousContainerPositionX = mContainerPositionX;
+            final int previousContainerPositionY = mContainerPositionY;
+
+            TextView.this.getLocationInWindow(mTempCoords);
+            mContainerPositionX = mTempCoords[0] + mPositionX;
+            mContainerPositionY = mTempCoords[1] + mPositionY;
+
+            return (previousContainerPositionX != mContainerPositionX ||
+                    previousContainerPositionY != mContainerPositionY);
         }
 
         public boolean onPreDraw() {
             if (updateContainerPosition()) {
+                if (mIsDragging) {
+                    if (mTempCoords[0] != mLastParentX || mTempCoords[1] != mLastParentY) {
+                        mTouchToWindowOffsetX += mTempCoords[0] - mLastParentX;
+                        mTouchToWindowOffsetY += mTempCoords[1] - mLastParentY;
+                        mLastParentX = mTempCoords[0];
+                        mLastParentY = mTempCoords[1];
+                    }
+                }
+
+                onHandleMoved();
+
                 if (isPositionVisible()) {
                     mContainer.update(mContainerPositionX, mContainerPositionY,
                             mRight - mLeft, mBottom - mTop);
@@ -9007,9 +8973,6 @@
                         dismiss();
                     }
                 }
-
-                // Hide paste popup as soon as the view is scrolled or moved
-                hideAssociatedPopupWindow();
             }
             return true;
         }
@@ -9076,8 +9039,8 @@
             return mIsDragging;
         }
 
-        void hideAssociatedPopupWindow() {
-            // No associated popup window by default
+        void onHandleMoved() {
+            // Does nothing by default
         }
 
         public void onDetached() {
@@ -9096,15 +9059,11 @@
         private Runnable mHider;
         private Runnable mPastePopupShower;
 
-        public InsertionHandleView() {
-            super();
-        }
-
         @Override
         public void show() {
             super.show();
             hideDelayed();
-            removePastePopupCallback();
+            hidePastePopupWindow();
         }
 
         public void show(int delayBeforePaste) {
@@ -9118,11 +9077,11 @@
                 if (mPastePopupShower == null) {
                     mPastePopupShower = new Runnable() {
                         public void run() {
-                            showAssociatedPopupWindow();
+                            showPastePopupWindow();
                         }
                     };
                 }
-                postDelayed(mPastePopupShower, delayBeforePaste);
+                TextView.this.postDelayed(mPastePopupShower, delayBeforePaste);
             }
         }
 
@@ -9132,11 +9091,6 @@
             onDetached();
         }
 
-        @Override
-        public void hide() {
-            super.hide();
-        }
-
         private void hideDelayed() {
             removeHiderCallback();
             if (mHider == null) {
@@ -9146,18 +9100,12 @@
                     }
                 };
             }
-            postDelayed(mHider, DELAY_BEFORE_FADE_OUT);
-        }
-
-        private void removePastePopupCallback() {
-            if (mPastePopupShower != null) {
-                removeCallbacks(mPastePopupShower);
-            }
+            TextView.this.postDelayed(mHider, DELAY_BEFORE_FADE_OUT);
         }
 
         private void removeHiderCallback() {
             if (mHider != null) {
-                removeCallbacks(mHider);
+                TextView.this.removeCallbacks(mHider);
             }
         }
 
@@ -9197,6 +9145,11 @@
                             }
                         }
                     }
+                    hideDelayed();
+                    break;
+
+                case MotionEvent.ACTION_CANCEL:
+                    hideDelayed();
                     break;
 
                 default:
@@ -9214,31 +9167,30 @@
         @Override
         public void updateOffset(int offset) {
             Selection.setSelection((Spannable) mText, offset);
-            positionAtCursorOffset(offset);
         }
 
         @Override
         public void updatePosition(int x, int y) {
-            final int previousOffset = getCurrentCursorOffset();
-            final int newOffset = getOffset(x, y);
-
-            if (newOffset != previousOffset) {
-                updateOffset(newOffset);
-                removePastePopupCallback();
-            }
-            hideDelayed();
+            updateOffset(getOffset(x, y));
         }
 
-        void showAssociatedPopupWindow() {
+        void showPastePopupWindow() {
             if (mPastePopupWindow == null) {
-                // Lazy initialisation: create when actually shown only.
                 mPastePopupWindow = new PastePopupWindow();
             }
             mPastePopupWindow.show();
         }
 
         @Override
-        void hideAssociatedPopupWindow() {
+        void onHandleMoved() {
+            removeHiderCallback();
+            hidePastePopupWindow();
+        }
+
+        void hidePastePopupWindow() {
+            if (mPastePopupShower != null) {
+                TextView.this.removeCallbacks(mPastePopupShower);
+            }
             if (mPastePopupWindow != null) {
                 mPastePopupWindow.hide();
             }
@@ -9247,15 +9199,11 @@
         @Override
         public void onDetached() {
             removeHiderCallback();
-            removePastePopupCallback();
+            hidePastePopupWindow();
         }
     }
 
     private class SelectionStartHandleView extends HandleView {
-        public SelectionStartHandleView() {
-            super();
-        }
-
         @Override
         protected void initDrawable() {
             if (mSelectHandleLeft == null) {
@@ -9274,7 +9222,6 @@
         @Override
         public void updateOffset(int offset) {
             Selection.setSelection((Spannable) mText, offset, getSelectionEnd());
-            positionAtCursorOffset(offset);
         }
 
         @Override
@@ -9290,15 +9237,10 @@
             if (offset >= selectionEnd) offset = selectionEnd - 1;
 
             Selection.setSelection((Spannable) mText, offset, selectionEnd);
-            positionAtCursorOffset(offset);
         }
     }
 
     private class SelectionEndHandleView extends HandleView {
-        public SelectionEndHandleView() {
-            super();
-        }
-
         @Override
         protected void initDrawable() {
             if (mSelectHandleRight == null) {
@@ -9317,7 +9259,6 @@
         @Override
         public void updateOffset(int offset) {
             Selection.setSelection((Spannable) mText, getSelectionStart(), offset);
-            positionAtCursorOffset(offset);
         }
 
         @Override
@@ -9333,7 +9274,6 @@
             if (offset <= selectionStart) offset = selectionStart + 1;
 
             Selection.setSelection((Spannable) mText, selectionStart, offset);
-            positionAtCursorOffset(offset);
         }
     }
 
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 029d690..423e735c 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -409,7 +409,9 @@
     }
 
     @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+
         int flags = DateUtils.FORMAT_SHOW_TIME;
         if (mIs24HourView) {
             flags |= DateUtils.FORMAT_24HOUR;
@@ -421,7 +423,6 @@
         String selectedDateUtterance = DateUtils.formatDateTime(mContext,
                 mTempCalendar.getTimeInMillis(), flags);
         event.getText().add(selectedDateUtterance);
-        return true;
     }
 
     private void updateHourControl() {
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 587d678..fa8eb51 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -457,11 +457,11 @@
                 mIconView.getPaddingTop() - mIconView.getPaddingBottom();
         int iconSize = res.getDimensionPixelSize(android.R.dimen.app_icon_size);
 
-        if (iconSize * DisplayMetrics.DENSITY_LOW > availableHeight) {
+        if (iconSize * DisplayMetrics.DENSITY_LOW >= availableHeight) {
             return DisplayMetrics.DENSITY_LOW;
-        } else if (iconSize * DisplayMetrics.DENSITY_MEDIUM > availableHeight) {
+        } else if (iconSize * DisplayMetrics.DENSITY_MEDIUM >= availableHeight) {
             return DisplayMetrics.DENSITY_MEDIUM;
-        } else if (iconSize * DisplayMetrics.DENSITY_HIGH > availableHeight) {
+        } else if (iconSize * DisplayMetrics.DENSITY_HIGH >= availableHeight) {
             return DisplayMetrics.DENSITY_HIGH;
         }
         return DisplayMetrics.DENSITY_XHIGH;
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 0a54e17..768b836 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -482,44 +482,28 @@
         return totalAdvance;
     }
 
-    static float getTextRunAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
+    static float getTextRunAdvances___CIIIII_FII(JNIEnv* env, jobject clazz, SkPaint* paint,
             jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
-            jint flags, jfloatArray advances, jint advancesIndex) {
+            jint flags, jfloatArray advances, jint advancesIndex, jint reserved) {
         jchar* textArray = env->GetCharArrayElements(text, NULL);
-        jfloat result = doTextRunAdvances(env, paint, textArray + contextIndex,
-            index - contextIndex, count, contextCount, flags, advances, advancesIndex);
+        jfloat result = (reserved == 0) ?
+                doTextRunAdvances(env, paint, textArray + contextIndex, index - contextIndex,
+                        count, contextCount, flags, advances, advancesIndex) :
+                doTextRunAdvancesICU(env, paint, textArray + contextIndex, index - contextIndex,
+                        count, contextCount, flags, advances, advancesIndex);
         env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
         return result;
     }
 
-    static float getTextRunAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
+    static float getTextRunAdvances__StringIIIII_FII(JNIEnv* env, jobject clazz, SkPaint* paint,
             jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags,
-            jfloatArray advances, jint advancesIndex) {
+            jfloatArray advances, jint advancesIndex, jint reserved) {
         const jchar* textArray = env->GetStringChars(text, NULL);
-        jfloat result = doTextRunAdvances(env, paint, textArray + contextStart,
-            start - contextStart, end - start, contextEnd - contextStart, flags, advances,
-            advancesIndex);
-        env->ReleaseStringChars(text, textArray);
-        return result;
-    }
-
-    static float getTextRunAdvancesICU___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
-            jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
-            jint flags, jfloatArray advances, jint advancesIndex) {
-        jchar* textArray = env->GetCharArrayElements(text, NULL);
-        jfloat result = doTextRunAdvancesICU(env, paint, textArray + contextIndex,
-            index - contextIndex, count, contextCount, flags, advances, advancesIndex);
-        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
-        return result;
-    }
-
-    static float getTextRunAdvancesICU__StringIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
-            jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags,
-            jfloatArray advances, jint advancesIndex) {
-        const jchar* textArray = env->GetStringChars(text, NULL);
-        jfloat result = doTextRunAdvancesICU(env, paint, textArray + contextStart,
-            start - contextStart, end - start, contextEnd - contextStart, flags, advances,
-            advancesIndex);
+        jfloat result = (reserved == 0) ?
+                doTextRunAdvances(env, paint, textArray + contextStart, start - contextStart,
+                        end - start, contextEnd - contextStart, flags, advances, advancesIndex) :
+                doTextRunAdvancesICU(env, paint, textArray + contextStart, start - contextStart,
+                        end - start, contextEnd - contextStart, flags, advances, advancesIndex);
         env->ReleaseStringChars(text, textArray);
         return result;
     }
@@ -816,14 +800,12 @@
     {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS},
     {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F},
     {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F},
-    {"native_getTextRunAdvances","(I[CIIIII[FI)F",
-        (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FI},
-    {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F",
-        (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI},
-    {"native_getTextRunAdvancesICU","(I[CIIIII[FI)F",
-        (void*) SkPaintGlue::getTextRunAdvancesICU___CIIIII_FI},
-    {"native_getTextRunAdvancesICU","(ILjava/lang/String;IIIII[FI)F",
-        (void*) SkPaintGlue::getTextRunAdvancesICU__StringIIIII_FI},
+    {"native_getTextRunAdvances","(I[CIIIII[FII)F",
+        (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FII},
+    {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FII)F",
+        (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FII},
+
+
     {"native_getTextGlyphs","(ILjava/lang/String;IIIII[C)I",
         (void*) SkPaintGlue::getTextGlyphs__StringIIIII_C},
     {"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C},
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 33825c7..05a4810 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -68,7 +68,7 @@
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"無法提供此服務。"</string>
     <string name="CLIRPermanent" msgid="5460892159398802465">"本機號碼顯示設定無法變更。"</string>
     <string name="RestrictedChangedTitle" msgid="5592189398956187498">"受限存取已變更"</string>
-    <string name="RestrictedOnData" msgid="8653794784690065540">"已封鎖資料傳輸服務。"</string>
+    <string name="RestrictedOnData" msgid="8653794784690065540">"已封鎖數據傳輸服務。"</string>
     <string name="RestrictedOnEmergency" msgid="6581163779072833665">"已封鎖緊急服務。"</string>
     <string name="RestrictedOnNormal" msgid="4953867011389750673">"已封鎖語音服務。"</string>
     <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"已封鎖所有語音服務。"</string>
@@ -700,15 +700,15 @@
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"1 個月前"</string>
   <plurals name="num_seconds_ago">
     <item quantity="one" msgid="4869870056547896011">"1 秒以前"</item>
-    <item quantity="other" msgid="3903706804349556379">"<xliff:g id="COUNT">%d</xliff:g> 秒以前"</item>
+    <item quantity="other" msgid="3903706804349556379">"<xliff:g id="COUNT">%d</xliff:g> 秒前"</item>
   </plurals>
   <plurals name="num_minutes_ago">
     <item quantity="one" msgid="3306787433088810191">"1 分鐘以前"</item>
-    <item quantity="other" msgid="2176942008915455116">"<xliff:g id="COUNT">%d</xliff:g> 分鐘以前"</item>
+    <item quantity="other" msgid="2176942008915455116">"<xliff:g id="COUNT">%d</xliff:g> 分鐘前"</item>
   </plurals>
   <plurals name="num_hours_ago">
     <item quantity="one" msgid="9150797944610821849">"1 小時以前"</item>
-    <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> 小時以前"</item>
+    <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</item>
   </plurals>
   <plurals name="last_num_days">
     <item quantity="other" msgid="3069992808164318268">"最近 <xliff:g id="COUNT">%d</xliff:g> 天"</item>
@@ -717,7 +717,7 @@
     <string name="older" msgid="5211975022815554840">"較舊"</string>
   <plurals name="num_days_ago">
     <item quantity="one" msgid="861358534398115820">"昨天"</item>
-    <item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> 天以前"</item>
+    <item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> 天前"</item>
   </plurals>
   <plurals name="in_num_seconds">
     <item quantity="one" msgid="2729745560954905102">"1 秒內"</item>
@@ -745,11 +745,11 @@
   </plurals>
   <plurals name="abbrev_num_hours_ago">
     <item quantity="one" msgid="4796212039724722116">"1 小時以前"</item>
-    <item quantity="other" msgid="6889970745748538901">"<xliff:g id="COUNT">%d</xliff:g> 小時以前"</item>
+    <item quantity="other" msgid="6889970745748538901">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</item>
   </plurals>
   <plurals name="abbrev_num_days_ago">
     <item quantity="one" msgid="8463161711492680309">"昨天"</item>
-    <item quantity="other" msgid="3453342639616481191">"<xliff:g id="COUNT">%d</xliff:g> 天以前"</item>
+    <item quantity="other" msgid="3453342639616481191">"<xliff:g id="COUNT">%d</xliff:g> 天前"</item>
   </plurals>
   <plurals name="abbrev_in_num_seconds">
     <item quantity="one" msgid="5842225370795066299">"1 秒內"</item>
@@ -922,14 +922,14 @@
     <string name="ext_media_unmountable_notification_title" product="nosdcard" msgid="2090046769532713563">"USB 儲存裝置已毀損"</string>
     <string name="ext_media_unmountable_notification_title" product="default" msgid="6410723906019100189">"SD 卡已損壞"</string>
     <string name="ext_media_unmountable_notification_message" product="nosdcard" msgid="529021299294450667">"USB 儲存裝置已損壞,您可能必須重新格式化。"</string>
-    <string name="ext_media_unmountable_notification_message" product="default" msgid="6902531775948238989">"SD 卡已毀損,您可能必須予以重新格式化。"</string>
-    <string name="ext_media_badremoval_notification_title" product="nosdcard" msgid="1661683031330951073">"USB 儲存裝置已意外移除"</string>
+    <string name="ext_media_unmountable_notification_message" product="default" msgid="6902531775948238989">"SD 卡已毀損,您可能必須重新格式化。"</string>
+    <string name="ext_media_badremoval_notification_title" product="nosdcard" msgid="1661683031330951073">"USB 儲存裝置未正常移除"</string>
     <string name="ext_media_badremoval_notification_title" product="default" msgid="6872152882604407837">"SD 卡未正常移除"</string>
     <string name="ext_media_badremoval_notification_message" product="nosdcard" msgid="4329848819865594241">"請先卸載 USB 儲存裝置,再將其移除,以免資料遺失。"</string>
     <string name="ext_media_badremoval_notification_message" product="default" msgid="7260183293747448241">"請先卸載 SD 卡,再將其移除,以免資料遺失。"</string>
     <string name="ext_media_safe_unmount_notification_title" product="nosdcard" msgid="3967973893270360230">"USB 儲存裝置已可安全移除"</string>
     <string name="ext_media_safe_unmount_notification_title" product="default" msgid="6729801130790616200">"可安全移除 SD 卡"</string>
-    <string name="ext_media_safe_unmount_notification_message" product="nosdcard" msgid="6142195361606493530">"您可安全移除 USB 儲存裝置了。"</string>
+    <string name="ext_media_safe_unmount_notification_message" product="nosdcard" msgid="6142195361606493530">"您現在可以安全地移除 USB 儲存裝置。"</string>
     <string name="ext_media_safe_unmount_notification_message" product="default" msgid="568841278138377604">"您現在可以安全地移除 SD 卡。"</string>
     <string name="ext_media_nomedia_notification_title" product="nosdcard" msgid="4486377230140227651">"USB 儲存裝置已移除"</string>
     <string name="ext_media_nomedia_notification_title" product="default" msgid="8902518030404381318">"已移除 SD 卡"</string>
diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
index d8898ae3e..7e13569 100644
--- a/docs/html/guide/topics/ui/actionbar.jd
+++ b/docs/html/guide/topics/ui/actionbar.jd
@@ -429,21 +429,18 @@
     private TabContentFragment mFragment;
 
     // Called to create an instance of the listener when adding a new tab
-    public TabListener(TabContentFragment fragment) {
+    public MyTabListener(TabContentFragment fragment) {
         mFragment = fragment;
     }
 
-    &#64;Override
     public void onTabSelected(Tab tab, FragmentTransaction ft) {
         ft.add(R.id.fragment_content, mFragment, null);
     }
 
-    &#64;Override
     public void onTabUnselected(Tab tab, FragmentTransaction ft) {
         ft.remove(mFragment);
     }
 
-    &#64;Override
     public void onTabReselected(Tab tab, FragmentTransaction ft) {
         // do nothing
     }
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 0949beb..962f22c 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1501,6 +1501,20 @@
     public float getTextRunAdvances(char[] chars, int index, int count,
             int contextIndex, int contextCount, int flags, float[] advances,
             int advancesIndex) {
+        return getTextRunAdvances(chars, index, count, contextIndex, contextCount, flags,
+                advances, advancesIndex, 0 /* use Harfbuzz*/);
+    }
+
+    /**
+     * Convenience overload that takes a char array instead of a
+     * String.
+     *
+     * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int, int)
+     * @hide
+     */
+    public float getTextRunAdvances(char[] chars, int index, int count,
+            int contextIndex, int contextCount, int flags, float[] advances,
+            int advancesIndex, int reserved) {
 
         if ((index | count | contextIndex | contextCount | advancesIndex
                 | (index - contextIndex)
@@ -1516,55 +1530,13 @@
 
         if (!mHasCompatScaling) {
             return native_getTextRunAdvances(mNativePaint, chars, index, count,
-                    contextIndex, contextCount, flags, advances, advancesIndex);
+                    contextIndex, contextCount, flags, advances, advancesIndex, reserved);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
         float res = native_getTextRunAdvances(mNativePaint, chars, index, count,
-                contextIndex, contextCount, flags, advances, advancesIndex);
-        setTextSize(oldSize);
-
-        if (advances != null) {
-            for (int i = advancesIndex, e = i + count; i < e; i++) {
-                advances[i] *= mInvCompatScaling;
-            }
-        }
-        return res * mInvCompatScaling; // assume errors are not significant
-    }
-
-    /**
-     * Convenience overload that takes a char array instead of a
-     * String.
-     *
-     * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
-     * @hide
-     */
-    public float getTextRunAdvancesICU(char[] chars, int index, int count,
-            int contextIndex, int contextCount, int flags, float[] advances,
-            int advancesIndex) {
-
-        if ((index | count | contextIndex | contextCount | advancesIndex
-                | (index - contextIndex)
-                | ((contextIndex + contextCount) - (index + count))
-                | (chars.length - (contextIndex + contextCount))
-                | (advances == null ? 0 :
-                    (advances.length - (advancesIndex + count)))) < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-        if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
-            throw new IllegalArgumentException("unknown flags value: " + flags);
-        }
-
-        if (!mHasCompatScaling) {
-            return native_getTextRunAdvancesICU(mNativePaint, chars, index, count,
-                    contextIndex, contextCount, flags, advances, advancesIndex);
-        }
-
-        final float oldSize = getTextSize();
-        setTextSize(oldSize * mCompatScaling);
-        float res = native_getTextRunAdvancesICU(mNativePaint, chars, index, count,
-                contextIndex, contextCount, flags, advances, advancesIndex);
+                contextIndex, contextCount, flags, advances, advancesIndex, reserved);
         setTextSize(oldSize);
 
         if (advances != null) {
@@ -1585,15 +1557,29 @@
     public float getTextRunAdvances(CharSequence text, int start, int end,
             int contextStart, int contextEnd, int flags, float[] advances,
             int advancesIndex) {
+        return getTextRunAdvances(text, start, end, contextStart, contextEnd, flags,
+                advances, advancesIndex, 0 /* use Harfbuzz */);
+    }
+
+    /**
+     * Convenience overload that takes a CharSequence instead of a
+     * String.
+     *
+     * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
+     * @hide
+     */
+    public float getTextRunAdvances(CharSequence text, int start, int end,
+            int contextStart, int contextEnd, int flags, float[] advances,
+            int advancesIndex, int reserved) {
 
         if (text instanceof String) {
             return getTextRunAdvances((String) text, start, end,
-                    contextStart, contextEnd, flags, advances, advancesIndex);
+                    contextStart, contextEnd, flags, advances, advancesIndex, reserved);
         }
         if (text instanceof SpannedString ||
             text instanceof SpannableString) {
             return getTextRunAdvances(text.toString(), start, end,
-                    contextStart, contextEnd, flags, advances, advancesIndex);
+                    contextStart, contextEnd, flags, advances, advancesIndex, reserved);
         }
         if (text instanceof GraphicsOperations) {
             return ((GraphicsOperations) text).getTextRunAdvances(start, end,
@@ -1605,42 +1591,7 @@
         char[] buf = TemporaryBuffer.obtain(contextLen);
         TextUtils.getChars(text, start, end, buf, 0);
         float result = getTextRunAdvances(buf, start - contextStart, len,
-                0, contextLen, flags, advances, advancesIndex);
-        TemporaryBuffer.recycle(buf);
-        return result;
-    }
-
-    /**
-     * Convenience overload that takes a CharSequence instead of a
-     * String.
-     *
-     * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
-     * @hide
-     */
-    public float getTextRunAdvancesICU(CharSequence text, int start, int end,
-            int contextStart, int contextEnd, int flags, float[] advances,
-            int advancesIndex) {
-
-        if (text instanceof String) {
-            return getTextRunAdvancesICU((String) text, start, end,
-                    contextStart, contextEnd, flags, advances, advancesIndex);
-        }
-        if (text instanceof SpannedString ||
-            text instanceof SpannableString) {
-            return getTextRunAdvancesICU(text.toString(), start, end,
-                    contextStart, contextEnd, flags, advances, advancesIndex);
-        }
-        if (text instanceof GraphicsOperations) {
-            return ((GraphicsOperations) text).getTextRunAdvancesICU(start, end,
-                    contextStart, contextEnd, flags, advances, advancesIndex, this);
-        }
-
-        int contextLen = contextEnd - contextStart;
-        int len = end - start;
-        char[] buf = TemporaryBuffer.obtain(contextLen);
-        TextUtils.getChars(text, start, end, buf, 0);
-        float result = getTextRunAdvancesICU(buf, start - contextStart, len,
-                0, contextLen, flags, advances, advancesIndex);
+                0, contextLen, flags, advances, advancesIndex, reserved);
         TemporaryBuffer.recycle(buf);
         return result;
     }
@@ -1689,6 +1640,55 @@
      */
     public float getTextRunAdvances(String text, int start, int end, int contextStart,
             int contextEnd, int flags, float[] advances, int advancesIndex) {
+        return getTextRunAdvances(text, start, end, contextStart, contextEnd, flags,
+                advances, advancesIndex, 0 /* use Harfbuzz*/);
+    }
+
+    /**
+     * Returns the total advance width for the characters in the run
+     * between start and end, and if advances is not null, the advance
+     * assigned to each of these characters (java chars).
+     *
+     * <p>The trailing surrogate in a valid surrogate pair is assigned
+     * an advance of 0.  Thus the number of returned advances is
+     * always equal to count, not to the number of unicode codepoints
+     * represented by the run.
+     *
+     * <p>In the case of conjuncts or combining marks, the total
+     * advance is assigned to the first logical character, and the
+     * following characters are assigned an advance of 0.
+     *
+     * <p>This generates the sum of the advances of glyphs for
+     * characters in a reordered cluster as the width of the first
+     * logical character in the cluster, and 0 for the widths of all
+     * other characters in the cluster.  In effect, such clusters are
+     * treated like conjuncts.
+     *
+     * <p>The shaping bounds limit the amount of context available
+     * outside start and end that can be used for shaping analysis.
+     * These bounds typically reflect changes in bidi level or font
+     * metrics across which shaping does not occur.
+     *
+     * @param text the text to measure
+     * @param start the index of the first character to measure
+     * @param end the index past the last character to measure
+     * @param contextStart the index of the first character to use for shaping context,
+     * must be <= start
+     * @param contextEnd the index past the last character to use for shaping context,
+     * must be >= end
+     * @param flags the flags to control the advances, either {@link #DIRECTION_LTR}
+     * or {@link #DIRECTION_RTL}
+     * @param advances array to receive the advances, must have room for all advances,
+     * can be null if only total advance is needed
+     * @param advancesIndex the position in advances at which to put the
+     * advance corresponding to the character at start
+     * @param reserved int reserved value
+     * @return the total advance
+     *
+     * @hide
+     */
+    public float getTextRunAdvances(String text, int start, int end, int contextStart,
+            int contextEnd, int flags, float[] advances, int advancesIndex, int reserved) {
 
         if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
                 | (start - contextStart) | (contextEnd - end)
@@ -1703,51 +1703,13 @@
 
         if (!mHasCompatScaling) {
             return native_getTextRunAdvances(mNativePaint, text, start, end,
-                    contextStart, contextEnd, flags, advances, advancesIndex);
+                    contextStart, contextEnd, flags, advances, advancesIndex, reserved);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
         float totalAdvance = native_getTextRunAdvances(mNativePaint, text, start, end,
-                contextStart, contextEnd, flags, advances, advancesIndex);
-        setTextSize(oldSize);
-
-        if (advances != null) {
-            for (int i = advancesIndex, e = i + (end - start); i < e; i++) {
-                advances[i] *= mInvCompatScaling;
-            }
-        }
-        return totalAdvance * mInvCompatScaling; // assume errors are insignificant
-    }
-
-    /**
-     * Temporary - DO NOT USE
-     *
-     * @hide
-     */
-    public float getTextRunAdvancesICU(String text, int start, int end, int contextStart,
-            int contextEnd, int flags, float[] advances, int advancesIndex) {
-
-        if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
-                | (start - contextStart) | (contextEnd - end)
-                | (text.length() - contextEnd)
-                | (advances == null ? 0 :
-                    (advances.length - advancesIndex - (end - start)))) < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-        if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
-            throw new IllegalArgumentException("unknown flags value: " + flags);
-        }
-
-        if (!mHasCompatScaling) {
-            return native_getTextRunAdvancesICU(mNativePaint, text, start, end,
-                    contextStart, contextEnd, flags, advances, advancesIndex);
-        }
-
-        final float oldSize = getTextSize();
-        setTextSize(oldSize * mCompatScaling);
-        float totalAdvance = native_getTextRunAdvances(mNativePaint, text, start, end,
-                contextStart, contextEnd, flags, advances, advancesIndex);
+                contextStart, contextEnd, flags, advances, advancesIndex, reserved);
         setTextSize(oldSize);
 
         if (advances != null) {
@@ -2017,17 +1979,10 @@
 
     private static native float native_getTextRunAdvances(int native_object,
             char[] text, int index, int count, int contextIndex, int contextCount,
-            int flags, float[] advances, int advancesIndex);
+            int flags, float[] advances, int advancesIndex, int reserved);
     private static native float native_getTextRunAdvances(int native_object,
             String text, int start, int end, int contextStart, int contextEnd,
-            int flags, float[] advances, int advancesIndex);
-
-    private static native float native_getTextRunAdvancesICU(int native_object,
-            char[] text, int index, int count, int contextIndex, int contextCount,
-            int flags, float[] advances, int advancesIndex);
-    private static native float native_getTextRunAdvancesICU(int native_object,
-            String text, int start, int end, int contextStart, int contextEnd,
-            int flags, float[] advances, int advancesIndex);
+            int flags, float[] advances, int advancesIndex, int reserved);
 
     private native int native_getTextRunCursor(int native_object, char[] text,
             int contextStart, int contextLength, int flags, int offset, int cursorOpt);
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
new file mode 100644
index 0000000..64f5a48
--- /dev/null
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security;
+
+import android.os.Bundle;
+
+/**
+ * Caller is required to ensure that {@link KeyStore#unlock
+ * KeyStore.unlock} was successful.
+ *
+ * @hide
+ */
+interface IKeyChainService {
+    byte[] getPrivate(String alias, String authToken);
+    byte[] getCertificate(String alias, String authToken);
+    byte[] getCaCertificate(String alias, String authToken);
+    String findIssuer(in Bundle cert);
+}
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
new file mode 100644
index 0000000..69847bf
--- /dev/null
+++ b/keystore/java/android/security/KeyChain.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import dalvik.system.CloseGuard;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import org.apache.harmony.xnet.provider.jsse.IndexedPKIXParameters;
+import org.apache.harmony.xnet.provider.jsse.SSLParametersImpl;
+
+/**
+ * @hide
+ */
+public final class KeyChain {
+
+    private static final String TAG = "KeyChain";
+
+    /**
+     * @hide Also used by KeyChainService implementation
+     */
+    public static final String ACCOUNT_TYPE = "com.android.keychain";
+
+    /**
+     * @hide Also used by KeyChainService implementation
+     */
+    // TODO This non-localized CA string to be removed when CAs moved out of keystore
+    public static final String CA_SUFFIX = " CA";
+
+    public static final String KEY_INTENT = "intent";
+
+    /**
+     * Intentionally not public to leave open the future possibility
+     * of hardware based keys. Callers should use {@link #toPrivateKey
+     * toPrivateKey} in order to convert a bundle to a {@code
+     * PrivateKey}
+     */
+    private static final String KEY_PKCS8 = "pkcs8";
+
+    /**
+     * Intentionally not public to leave open the future possibility
+     * of hardware based certs. Callers should use {@link
+     * #toCertificate toCertificate} in order to convert a bundle to a
+     * {@code PrivateKey}
+     */
+    private static final String KEY_X509 = "x509";
+
+    /**
+     * Returns an {@code Intent} for use with {@link
+     * android.app.Activity#startActivityForResult
+     * startActivityForResult}. The result will be returned via {@link
+     * android.app.Activity#onActivityResult onActivityResult} with
+     * {@link android.app.Activity#RESULT_OK RESULT_OK} and the alias
+     * in the returned {@code Intent}'s extra data with key {@link
+     * android.content.Intent#EXTRA_TEXT Intent.EXTRA_TEXT}.
+     */
+    public static Intent chooseAlias() {
+        return new Intent("com.android.keychain.CHOOSER");
+    }
+
+    /**
+     * Returns a new {@code KeyChain} instance. When the caller is
+     * done using the {@code KeyChain}, it must be closed with {@link
+     * #close()} or resource leaks will occur.
+     */
+    public static KeyChain getInstance(Context context) throws InterruptedException {
+        return new KeyChain(context);
+    }
+
+    private final AccountManager mAccountManager;
+
+    private final Object mServiceLock = new Object();
+    private IKeyChainService mService;
+    private boolean mIsBound;
+
+    private Account mAccount;
+
+    private ServiceConnection mServiceConnection = new ServiceConnection() {
+        @Override public void onServiceConnected(ComponentName name, IBinder service) {
+            synchronized (mServiceLock) {
+                mService = IKeyChainService.Stub.asInterface(service);
+                mServiceLock.notifyAll();
+
+                // Account is created if necessary during binding of the IKeyChainService
+                mAccount = mAccountManager.getAccountsByType(ACCOUNT_TYPE)[0];
+            }
+        }
+
+        @Override public void onServiceDisconnected(ComponentName name) {
+            synchronized (mServiceLock) {
+                mService = null;
+            }
+        }
+    };
+
+    private final Context mContext;
+
+    private final CloseGuard mGuard = CloseGuard.get();
+
+    private KeyChain(Context context) throws InterruptedException {
+        if (context == null) {
+            throw new NullPointerException("context == null");
+        }
+        mContext = context;
+        ensureNotOnMainThread();
+        mAccountManager = AccountManager.get(mContext);
+        mIsBound = mContext.bindService(new Intent(IKeyChainService.class.getName()),
+                                        mServiceConnection,
+                                        Context.BIND_AUTO_CREATE);
+        if (!mIsBound) {
+            throw new AssertionError();
+        }
+        synchronized (mServiceLock) {
+            // there is a race between binding on this thread and the
+            // callback on the main thread. wait until binding is done
+            // to be sure we have the mAccount initialized.
+            if (mService == null) {
+                mServiceLock.wait();
+            }
+        }
+        mGuard.open("close");
+    }
+
+    /**
+     * {@code Bundle} will contain {@link #KEY_INTENT} if user needs
+     * to confirm application access to requested key. In the alias
+     * does not exist or there is an error, null is
+     * returned. Otherwise the {@code Bundle} contains information
+     * representing the private key which can be interpreted with
+     * {@link #toPrivateKey toPrivateKey}.
+     *
+     * non-null alias
+     */
+    public Bundle getPrivate(String alias) {
+        return get(alias, Credentials.USER_PRIVATE_KEY);
+    }
+
+    public Bundle getCertificate(String alias) {
+        return get(alias, Credentials.USER_CERTIFICATE);
+    }
+
+    public Bundle getCaCertificate(String alias) {
+        return get(alias, Credentials.CA_CERTIFICATE);
+    }
+
+    private Bundle get(String alias, String type) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+        ensureNotOnMainThread();
+
+        String authAlias = (type.equals(Credentials.CA_CERTIFICATE)) ? (alias + CA_SUFFIX) : alias;
+        AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(mAccount,
+                                                                           authAlias,
+                                                                           false,
+                                                                           null,
+                                                                           null);
+        Bundle bundle;
+        try {
+            bundle = future.getResult();
+        } catch (OperationCanceledException e) {
+            throw new AssertionError(e);
+        } catch (IOException e) {
+            throw new AssertionError(e);
+        } catch (AuthenticatorException e) {
+            throw new AssertionError(e);
+        }
+        Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
+        if (intent != null) {
+            Bundle result = new Bundle();
+            // we don't want this Eclair compatability flag,
+            // it will prevent onActivityResult from being called
+            intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
+            result.putParcelable(KEY_INTENT, intent);
+            return result;
+        }
+        String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
+        if (authToken == null) {
+            throw new AssertionError("Invalid authtoken");
+        }
+
+        byte[] bytes;
+        try {
+            if (type.equals(Credentials.USER_PRIVATE_KEY)) {
+                bytes = mService.getPrivate(alias, authToken);
+            } else if (type.equals(Credentials.USER_CERTIFICATE)) {
+                bytes = mService.getCertificate(alias, authToken);
+            } else if (type.equals(Credentials.CA_CERTIFICATE)) {
+                bytes = mService.getCaCertificate(alias, authToken);
+            } else {
+                throw new AssertionError();
+            }
+        } catch (RemoteException e) {
+            throw new AssertionError(e);
+        }
+        if (bytes == null) {
+            throw new AssertionError();
+        }
+        Bundle result = new Bundle();
+        if (type.equals(Credentials.USER_PRIVATE_KEY)) {
+            result.putByteArray(KEY_PKCS8, bytes);
+        } else if (type.equals(Credentials.USER_CERTIFICATE)) {
+            result.putByteArray(KEY_X509, bytes);
+        } else if (type.equals(Credentials.CA_CERTIFICATE)) {
+            result.putByteArray(KEY_X509, bytes);
+        } else {
+            throw new AssertionError();
+        }
+        return result;
+    }
+
+    public static PrivateKey toPrivateKey(Bundle bundle) {
+        byte[] bytes = bundle.getByteArray(KEY_PKCS8);
+        if (bytes == null) {
+            throw new IllegalArgumentException("not a private key bundle");
+        }
+        try {
+            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+            return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes));
+        } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError(e);
+        } catch (InvalidKeySpecException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    public static Bundle fromPrivateKey(PrivateKey privateKey) {
+        Bundle bundle = new Bundle();
+        String format = privateKey.getFormat();
+        if (!format.equals("PKCS#8")) {
+            throw new IllegalArgumentException("Unsupported private key format " + format);
+        }
+        bundle.putByteArray(KEY_PKCS8, privateKey.getEncoded());
+        return bundle;
+    }
+
+    public static X509Certificate toCertificate(Bundle bundle) {
+        byte[] bytes = bundle.getByteArray(KEY_X509);
+        if (bytes == null) {
+            throw new IllegalArgumentException("not a certificate bundle");
+        }
+        try {
+            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+            Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
+            return (X509Certificate) cert;
+        } catch (CertificateException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    public static Bundle fromCertificate(Certificate cert) {
+        Bundle bundle = new Bundle();
+        String type = cert.getType();
+        if (!type.equals("X.509")) {
+            throw new IllegalArgumentException("Unsupported certificate type " + type);
+        }
+        try {
+            bundle.putByteArray(KEY_X509, cert.getEncoded());
+        } catch (CertificateEncodingException e) {
+            throw new AssertionError(e);
+        }
+        return bundle;
+    }
+
+    private void ensureNotOnMainThread() {
+        Looper looper = Looper.myLooper();
+        if (looper != null && looper == mContext.getMainLooper()) {
+            throw new IllegalStateException(
+                    "calling this from your main thread can lead to deadlock");
+        }
+    }
+
+    public Bundle findIssuer(X509Certificate cert) {
+        if (cert == null) {
+            throw new NullPointerException("cert == null");
+        }
+        ensureNotOnMainThread();
+
+        // check and see if the issuer is already known to the default IndexedPKIXParameters
+        IndexedPKIXParameters index = SSLParametersImpl.getDefaultIndexedPKIXParameters();
+        try {
+            TrustAnchor anchor = index.findTrustAnchor(cert);
+            if (anchor != null && anchor.getTrustedCert() != null) {
+                X509Certificate ca = anchor.getTrustedCert();
+                return fromCertificate(ca);
+            }
+        } catch (CertPathValidatorException ignored) {
+        }
+
+        // otherwise, it might be a user installed CA in the keystore
+        String alias;
+        try {
+            alias = mService.findIssuer(fromCertificate(cert));
+        } catch (RemoteException e) {
+            throw new AssertionError(e);
+        }
+        if (alias == null) {
+            Log.w(TAG, "Lookup failed for issuer");
+            return null;
+        }
+
+        Bundle bundle = get(alias, Credentials.CA_CERTIFICATE);
+        Intent intent = bundle.getParcelable(KEY_INTENT);
+        if (intent != null) {
+            // permission still required
+            return bundle;
+        }
+        // add the found CA to the index for next time
+        X509Certificate ca = toCertificate(bundle);
+        index.index(new TrustAnchor(ca, null));
+        return bundle;
+    }
+
+    public void close() {
+        if (mIsBound) {
+            mContext.unbindService(mServiceConnection);
+            mIsBound = false;
+            mGuard.close();
+        }
+    }
+
+    protected void finalize() throws Throwable {
+        // note we don't close, we just warn.
+        // shouldn't be doing I/O in a finalizer,
+        // which the unbind would cause.
+        try {
+            if (mGuard != null) {
+                mGuard.warnIfOpen();
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+}
diff --git a/libs/rs/driver/rsdRuntimeMath.cpp b/libs/rs/driver/rsdRuntimeMath.cpp
index bd1fd0e..093e311 100644
--- a/libs/rs/driver/rsdRuntimeMath.cpp
+++ b/libs/rs/driver/rsdRuntimeMath.cpp
@@ -243,13 +243,15 @@
 static float SC_randf(float max) {
     float r = (float)rand();
     r *= max;
-    return r / RAND_MAX;
+    r /= RAND_MAX;
+    return r;
 }
 
 static float SC_randf2(float min, float max) {
     float r = (float)rand();
+    r /= RAND_MAX;
     r = r * (max - min) + min;
-    return r / RAND_MAX;
+    return r;
 }
 
 static int SC_randi(int max) {
diff --git a/libs/rs/rsMatrix4x4.cpp b/libs/rs/rsMatrix4x4.cpp
index 2d90a98..f34af47 100644
--- a/libs/rs/rsMatrix4x4.cpp
+++ b/libs/rs/rsMatrix4x4.cpp
@@ -305,3 +305,10 @@
     out[2] = (m[2] * in[0]) + (m[6] * in[1]) + (m[10] * in[2]) + m[14];
     out[3] = (m[3] * in[0]) + (m[7] * in[1]) + (m[11] * in[2]) + m[15];
 }
+
+void Matrix4x4::logv(const char *s) const {
+    LOGV("%s {%f, %f, %f, %f",  s, m[0], m[4], m[8], m[12]);
+    LOGV("%s  %f, %f, %f, %f",  s, m[1], m[5], m[9], m[13]);
+    LOGV("%s  %f, %f, %f, %f",  s, m[2], m[6], m[10], m[14]);
+    LOGV("%s  %f, %f, %f, %f}", s, m[3], m[7], m[11], m[15]);
+}
diff --git a/libs/rs/rsMatrix4x4.h b/libs/rs/rsMatrix4x4.h
index abf34a3..d30184f 100644
--- a/libs/rs/rsMatrix4x4.h
+++ b/libs/rs/rsMatrix4x4.h
@@ -54,6 +54,7 @@
     bool inverseTranspose();
     void transpose();
 
+    void logv(const char *s) const;
 
 
     void multiply(const rs_matrix4x4 *rhs) {
diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp
index bfbdd77..5696433 100755
--- a/media/jni/mediaeditor/VideoEditorClasses.cpp
+++ b/media/jni/mediaeditor/VideoEditorClasses.cpp
@@ -28,7 +28,6 @@
 #include <M4OSA_FileWriter.h>
 #include <M4OSA_Memory.h>
 #include <M4OSA_Debug.h>
-#include <M4OSA_String.h>
 #include <M4OSA_Thread.h>
 #include <M4VSS3GPP_API.h>
 #include <M4xVSS_API.h>
diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp
index 0376806..c95a0c2 100755
--- a/media/jni/mediaeditor/VideoEditorMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorMain.cpp
@@ -41,15 +41,12 @@
 #include <M4OSA_FileCommon.h>
 #include <M4OSA_FileReader.h>
 #include <M4OSA_FileWriter.h>
-#include <M4OSA_FileExtra.h>
 #include <M4OSA_Memory.h>
-#include <M4OSA_String.h>
 #include <M4OSA_Thread.h>
 #include <M4xVSS_API.h>
 #include <M4VSS3GPP_ErrorCodes.h>
 #include <M4MCS_API.h>
 #include <M4MCS_ErrorCodes.h>
-#include <M4MDP_API.h>
 #include <M4READER_Common.h>
 #include <M4WRITER_common.h>
 };
diff --git a/media/jni/mediaeditor/VideoEditorOsal.cpp b/media/jni/mediaeditor/VideoEditorOsal.cpp
index 616c2fe..53e7de1 100755
--- a/media/jni/mediaeditor/VideoEditorOsal.cpp
+++ b/media/jni/mediaeditor/VideoEditorOsal.cpp
@@ -25,7 +25,6 @@
 #include <M4OSA_FileReader.h>
 #include <M4OSA_FileWriter.h>
 #include <M4OSA_Memory.h>
-#include <M4OSA_String.h>
 #include <M4OSA_Thread.h>
 #include <M4xVSS_API.h>
 #include <M4VSS3GPP_ErrorCodes.h>
@@ -82,14 +81,6 @@
     VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_FILE_BAD_MODE_ACCESS                             ),
     VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_FILE_INVALID_POSITION                            ),
 
-    // M4OSA_String.h
-    VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_BAD_STRING                                   ),
-    VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_CONV_FAILED                                  ),
-    VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_OVERFLOW                                     ),
-    VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_BAD_ARGS                                     ),
-    VIDEOEDIT_OSAL_RESULT_INIT(M4WAR_STR_OVERFLOW                                     ),
-    VIDEOEDIT_OSAL_RESULT_INIT(M4WAR_STR_NOT_FOUND                                    ),
-
     // M4OSA_Thread.h
     VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_THREAD_NOT_STARTED                               ),
 
diff --git a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
index 39221f3..9de7207 100755
--- a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
@@ -34,13 +34,11 @@
 #include <M4OSA_FileReader.h>
 #include <M4OSA_FileWriter.h>
 #include <M4OSA_Memory.h>
-#include <M4OSA_String.h>
 #include <M4OSA_Thread.h>
 #include <M4VSS3GPP_API.h>
 #include <M4VSS3GPP_ErrorCodes.h>
 #include <M4MCS_API.h>
 #include <M4MCS_ErrorCodes.h>
-#include <M4MDP_API.h>
 #include <M4READER_Common.h>
 #include <M4WRITER_common.h>
 #include <M4DECODER_Common.h>
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index f9db1a1..01d0a92 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -2028,14 +2028,18 @@
             size_t dstOffset = 0;
 
             while (srcOffset < size) {
-                CHECK(srcOffset + mNALLengthSize <= size);
-                size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
-                srcOffset += mNALLengthSize;
+                bool isMalFormed = (srcOffset + mNALLengthSize > size);
+                size_t nalLength = 0;
+                if (!isMalFormed) {
+                    nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
+                    srcOffset += mNALLengthSize;
+                    isMalFormed = srcOffset + nalLength > size;
+                }
 
-                if (srcOffset + nalLength > size) {
+                if (isMalFormed) {
+                    LOGE("Video is malformed");
                     mBuffer->release();
                     mBuffer = NULL;
-
                     return ERROR_MALFORMED;
                 }
 
diff --git a/opengl/libs/GLES2_dbg/generate_api_cpp.py b/opengl/libs/GLES2_dbg/generate_api_cpp.py
index aeba213..96cde57 100755
--- a/opengl/libs/GLES2_dbg/generate_api_cpp.py
+++ b/opengl/libs/GLES2_dbg/generate_api_cpp.py
@@ -38,7 +38,7 @@
 "glShaderSource", "glTexImage2D", "glTexSubImage2D"]
 
     # these also needs to be forwarded to DbgContext
-    contextFunctions = ["glUseProgram", "glEnableVertexAttribArray", "glDisableVertexAttribArray", 
+    contextFunctions = ["glUseProgram", "glEnableVertexAttribArray", "glDisableVertexAttribArray",
 "glVertexAttribPointer", "glBindBuffer", "glBufferData", "glBufferSubData", "glDeleteBuffers",]
 
     for line in lines:
@@ -114,10 +114,10 @@
                     else:
                         getData += "    msg.mutable_data()->assign(reinterpret_cast<const char *>(%s), %s * sizeof(%s));" % (paramName, annotation, paramType)
                     paramType += "*"
-                else:     
+                else:
                     if paramType == "GLfloat" or paramType == "GLclampf" or paramType.find("*") >= 0:
                         setMsgParameters += "    msg.set_arg%d(ToInt(%s));\n" % (paramIndex, paramName)
-                    else: 
+                    else:
                         setMsgParameters += "    msg.set_arg%d(%s);\n" % (paramIndex, paramName)
                 if paramIndex < len(parameters) - 1:
                         arguments += ', '
@@ -156,7 +156,7 @@
     } caller;"""
             print setCallerMembers
             print setMsgParameters
-    
+
             if line.find("*") >= 0 or line.find(":") >= 0:
                 print "    // FIXME: check for pointer usage"
             if inout in ["in", "inout"]:
diff --git a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
index 57e008c..535b13e 100755
--- a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
+++ b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
@@ -25,7 +25,7 @@
             line = line.split(",")[1].strip() #extract EGL function name
             output.write("        %s = %d;\n" % (line, i))
             i += 1
-    return i    
+    return i
 
 
 def generate_gl_entries(output,lines,i):
@@ -118,7 +118,7 @@
     optional int32 arg4 = 16;
     optional int32 arg5 = 17;
     optional int32 arg6 = 18;
-    optional int32 arg7 = 19;
+    optional int32 arg7 = 19; // glDrawArrays/Elements sets this to active number of attributes
     optional int32 arg8 = 20;
 
     optional bytes data = 10; // variable length data used for GL call
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 879f1a8..73003c8 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -511,7 +511,7 @@
             }
 
             // This will populate st.shownPanelView
-            if (!initializePanelContent(st) || (st.shownPanelView == null)) {
+            if (!initializePanelContent(st) || !st.hasPanelItems()) {
                 return;
             }
 
@@ -2976,6 +2976,16 @@
             refreshDecorView = false;
         }
 
+        public boolean hasPanelItems() {
+            if (shownPanelView == null) return false;
+
+            if (isInExpandedMode) {
+                return expandedMenuPresenter.getAdapter().getCount() > 0;
+            } else {
+                return ((ViewGroup) shownPanelView).getChildCount() > 0;
+            }
+        }
+
         /**
          * Unregister and free attached MenuPresenters. They will be recreated as needed.
          */
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 65321b7..a37ccc7 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2615,6 +2615,26 @@
         }
     }
 
+    public int getLockedRotationLw() {
+        synchronized (mLock) {
+            if (false) {
+                // Not yet working.
+                if (mHdmiPlugged) {
+                    return Surface.ROTATION_0;
+                } else if (mLidOpen == LID_OPEN) {
+                    return mLidOpenRotation;
+                } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR && mCarDockRotation >= 0) {
+                    return mCarDockRotation;
+                } else if (mDockMode == Intent.EXTRA_DOCK_STATE_DESK && mDeskDockRotation >= 0) {
+                    return mDeskDockRotation;
+                } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
+                    return mUserRotation;
+                }
+            }
+            return -1;
+        }
+    }
+
     private int getCurrentLandscapeRotation(int lastRotation) {
         // if the user has locked rotation, we ignore the sensor 
         if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
index ced8feb..8ba0a0b 100644
--- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -20,8 +20,8 @@
 
 import android.content.Context;
 import android.util.Slog;
+import android.view.InputDevice;
 import android.view.InputEvent;
-import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.WindowManagerPolicy;
 
@@ -32,10 +32,35 @@
  */
 public class AccessibilityInputFilter extends InputFilter {
     private static final String TAG = "AccessibilityInputFilter";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private final Context mContext;
 
+    /**
+     * This is an interface for explorers that take a {@link MotionEvent}
+     * stream and perform touch exploration of the screen content.
+     */
+    public interface Explorer {
+        /**
+         * Handles a {@link MotionEvent}.
+         *
+         * @param event The event to handle.
+         * @param policyFlags The policy flags associated with the event.
+         */
+        public void onMotionEvent(MotionEvent event, int policyFlags);
+
+        /**
+         * Requests that the explorer clears its internal state.
+         *
+         * @param event The last received event.
+         * @param policyFlags The policy flags associated with the event.
+         */
+        public void clear(MotionEvent event, int policyFlags);
+    }
+
+    private TouchExplorer mTouchExplorer;
+    private int mTouchscreenSourceDeviceId;
+
     public AccessibilityInputFilter(Context context) {
         super(context.getMainLooper());
         mContext = context;
@@ -60,27 +85,27 @@
     @Override
     public void onInputEvent(InputEvent event, int policyFlags) {
         if (DEBUG) {
-            Slog.d(TAG, "Accessibility input filter received input event: "
-                    + event + ", policyFlags=0x" + Integer.toHexString(policyFlags));
+            Slog.d(TAG, "Received event: " + event + ", policyFlags=0x" 
+                    + Integer.toHexString(policyFlags));
         }
-
-        // To prove that this is working as intended, we will silently transform
-        // Q key presses into non-repeating Z's as part of this stub implementation.
-        // TODO: Replace with the real thing.
-        if (event instanceof KeyEvent) {
-            final KeyEvent keyEvent = (KeyEvent)event;
-            if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_Q) {
-                if (keyEvent.getRepeatCount() == 0) {
-                    sendInputEvent(new KeyEvent(keyEvent.getDownTime(), keyEvent.getEventTime(),
-                            keyEvent.getAction(), KeyEvent.KEYCODE_Z, keyEvent.getRepeatCount(),
-                            keyEvent.getMetaState(), keyEvent.getDeviceId(), keyEvent.getScanCode(),
-                            keyEvent.getFlags(), keyEvent.getSource()),
-                            policyFlags | WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
+        if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
+            MotionEvent motionEvent = (MotionEvent) event;
+            int deviceId = event.getDeviceId();
+            if (mTouchscreenSourceDeviceId != deviceId) {
+                mTouchscreenSourceDeviceId = deviceId;
+                if (mTouchExplorer != null) {
+                    mTouchExplorer.clear(motionEvent, policyFlags);
+                } else {
+                    mTouchExplorer = new TouchExplorer(this, mContext);
                 }
-                return;
             }
+            if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) != 0) {
+                mTouchExplorer.onMotionEvent(motionEvent, policyFlags);
+            } else {
+                mTouchExplorer.clear(motionEvent, policyFlags);
+            }
+        } else {
+            super.onInputEvent(event, policyFlags);
         }
-
-        super.onInputEvent(event, policyFlags);
     }
 }
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7a483aa..1ad8047 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -56,6 +56,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -74,6 +75,8 @@
 public class AccessibilityManagerService extends IAccessibilityManager.Stub
         implements HandlerCaller.Callback {
 
+    private static final boolean DEBUG = false;
+
     private static final String LOG_TAG = "AccessibilityManagerService";
 
     private static int sIdCounter = 0;
@@ -102,6 +105,9 @@
 
     private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
 
+    private final SparseArray<List<ServiceInfo>> mFeedbackTypeToEnabledServicesMap =
+        new SparseArray<List<ServiceInfo>>();
+
     private PackageManager mPackageManager;
 
     private int mHandledFeedbackTypes = 0;
@@ -211,7 +217,6 @@
                         }
 
                         manageServicesLocked();
-                        updateInputFilterLocked();
                     }
                     
                     return;
@@ -252,7 +257,6 @@
                             unbindAllServicesLocked();
                         }
                         updateClientsLocked();
-                        updateInputFilterLocked();
                     }
                 }
             });
@@ -300,6 +304,16 @@
         }
     }
 
+    public List<ServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
+        synchronized (mLock) {
+            List<ServiceInfo> enabledServices = mFeedbackTypeToEnabledServicesMap.get(feedbackType);
+            if (enabledServices == null) {
+                return Collections.emptyList();
+            }
+            return enabledServices;
+        }
+    }
+
     public void interrupt() {
         synchronized (mLock) {
             for (int i = 0, count = mServices.size(); i < count; i++) {
@@ -339,6 +353,8 @@
                     }
                     service.mNotificationTimeout = info.notificationTimeout;
                     service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
+
+                    updateStateOnEnabledService(service);
                 }
                 return;
             default:
@@ -449,7 +465,7 @@
 
         try {
             listener.onAccessibilityEvent(event);
-            if (false) {
+            if (DEBUG) {
                 Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
             }
         } catch (RemoteException re) {
@@ -469,10 +485,11 @@
      * @return True if the service was removed, false otherwise.
      */
     private boolean removeDeadServiceLocked(Service service) {
-        if (false) {
+        if (DEBUG) {
             Slog.i(LOG_TAG, "Dead service " + service.mService + " removed");
         }
         mHandler.removeMessages(service.mId);
+        updateStateOnDisabledService(service);
         return mServices.remove(service);
     }
 
@@ -593,7 +610,7 @@
             if (isEnabled) {
                 if (enabledServices.contains(componentName)) {
                     if (service == null) {
-                        service = new Service(componentName);
+                        service = new Service(componentName, intalledService);
                     }
                     service.bind();
                 } else if (!enabledServices.contains(componentName)) {
@@ -644,6 +661,47 @@
     }
 
     /**
+     * Updates the set of enabled services for a given feedback type and
+     * if more than one of them provides spoken feedback enables touch
+     * exploration.
+     *
+     * @param service An enable service.
+     */
+    private void updateStateOnEnabledService(Service service) {
+        int feedbackType = service.mFeedbackType;
+        List<ServiceInfo> enabledServices = mFeedbackTypeToEnabledServicesMap.get(feedbackType);
+        if (enabledServices == null) {
+            enabledServices = new ArrayList<ServiceInfo>();
+            mFeedbackTypeToEnabledServicesMap.put(feedbackType, enabledServices);
+        }
+        enabledServices.add(service.mServiceInfo);
+
+        // We enable touch exploration if at least one
+        // enabled service provides spoken feedback.
+        if (enabledServices.size() > 0
+                && service.mFeedbackType == AccessibilityServiceInfo.FEEDBACK_SPOKEN) {
+            updateClientsLocked();
+            updateInputFilterLocked();
+        }
+    }
+
+    private void updateStateOnDisabledService(Service service) {
+        List<ServiceInfo> enabledServices =
+            mFeedbackTypeToEnabledServicesMap.get(service.mFeedbackType);
+        if (enabledServices == null) {
+            return;
+        }
+        enabledServices.remove(service.mServiceInfo);
+        // We disable touch exploration if no
+        // enabled service provides spoken feedback.
+        if (enabledServices.isEmpty()
+                && service.mFeedbackType == AccessibilityServiceInfo.FEEDBACK_SPOKEN) {
+            updateClientsLocked();
+            updateInputFilterLocked();
+        }
+    }
+
+    /**
      * This class represents an accessibility service. It stores all per service
      * data required for the service management, provides API for starting/stopping the
      * service and is responsible for adding/removing the service in the data structures
@@ -654,6 +712,8 @@
     class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection {
         int mId = 0;
 
+        ServiceInfo mServiceInfo;
+
         IBinder mService;
 
         IEventListener mServiceInterface;
@@ -678,9 +738,10 @@
         final SparseArray<AccessibilityEvent> mPendingEvents =
             new SparseArray<AccessibilityEvent>();
 
-        Service(ComponentName componentName) {
+        Service(ComponentName componentName, ServiceInfo serviceInfo) {
             mId = sIdCounter++;
             mComponentName = componentName;
+            mServiceInfo = serviceInfo;
             mIntent = new Intent().setComponent(mComponentName);
             mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                     com.android.internal.R.string.accessibility_binding_label);
@@ -712,6 +773,7 @@
                 mContext.unbindService(this);
                 mComponentNameToServiceMap.remove(mComponentName);
                 mServices.remove(this);
+                updateStateOnDisabledService(this);
                 return true;
             }
             return false;
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
new file mode 100644
index 0000000..4ba6060
--- /dev/null
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -0,0 +1,1540 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+package 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 com.android.server.accessibility.AccessibilityInputFilter.Explorer;
+import com.android.server.wm.InputFilter;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.view.WindowManagerPolicy;
+import android.view.MotionEvent.PointerCoords;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import java.util.Arrays;
+
+/**
+ * This class is a strategy for performing touch exploration. It
+ * transforms the motion event stream by modifying, adding, replacing,
+ * 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
+ *          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>
+ * <ol>
+ *
+ * @hide
+ */
+public class TouchExplorer implements Explorer {
+    private static final boolean DEBUG = false;
+
+    // Tag for logging received events.
+    private static final String LOG_TAG_RECEIVED = "TouchExplorer-RECEIVED";
+    // Tag for logging injected events.
+    private static final String LOG_TAG_INJECTED = "TouchExplorer-INJECTED";
+    // Tag for logging the current state.
+    private static final String LOG_TAG_STATE = "TouchExplorer-STATE";
+
+    // States this explorer can be in.
+    private static final int STATE_TOUCH_EXPLORING = 0x00000001;
+    private static final int STATE_DRAGGING = 0x00000002;
+    private static final int STATE_DELEGATING = 0x00000004;
+
+    // Human readable symbolic names for the states of the explorer.
+    private static final SparseArray<String> sStateSymbolicNames = new SparseArray<String>();
+    static {
+        SparseArray<String> symbolicNames = sStateSymbolicNames;
+        symbolicNames.append(STATE_TOUCH_EXPLORING, "STATE_TOUCH_EXPLORING");
+        symbolicNames.append(STATE_DRAGGING, "STATE_DRAGING");
+        symbolicNames.append(STATE_DELEGATING, "STATE_DELEGATING");
+    }
+
+    // Invalid pointer ID.
+    private static final int INVALID_POINTER_ID = -1;
+
+    // The coefficient by which to multiply
+    // ViewConfiguration.#getScaledTouchExplorationTapSlop()
+    // to compute #mDraggingDistance.
+    private static final int COEFFICIENT_DRAGGING_DISTANCE = 2;
+
+    // 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;
+
+    // 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)
+    private static final int MAX_POINTER_COUNT = 32;
+
+    // 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 MIN_ANGLE_COS = 0.866025404f; // cos(pi/6)
+
+    // The delay for sending a hover enter event.
+    private static final long DELAY_SEND_HOVER_MOVE = 200;
+
+    // Temporary array for storing pointer IDs.
+    private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT];
+
+    // Temporary array for mapping new to old pointer IDs while filtering inactive pointers.
+    private final int [] mTempNewToOldPointerIndexMap = new int[MAX_POINTER_COUNT];
+
+    // Temporary array for storing PointerCoords
+    private final PointerCoords[] mTempPointerCoords= new PointerCoords[MAX_POINTER_COUNT];
+
+    // The maximal distance between two pointers so they are
+    // considered to be performing a drag operation.
+    private final float mDraggingDistance;
+
+    // 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;
+
+    // Context handle for accessing resources.
+    private final Context mContext;
+
+    // The InputFilter this tracker is associated with i.e. the filter
+    // which delegates event processing to this touch explorer.
+    private final InputFilter mInputFilter;
+
+    // Helper class for tracking pointers on the screen, for example which
+    // pointers are down, which are active, etc.
+    private final PointerTracker mPointerTracker;
+
+    // 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;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param inputFilter The input filter associated with this explorer.
+     * @param context A context handle for accessing resources.
+     */
+    public TouchExplorer(InputFilter inputFilter, Context context) {
+        mInputFilter = inputFilter;
+        mTouchExplorationTapSlop =
+            ViewConfiguration.get(context).getScaledTouchExplorationTapSlop();
+        mDraggingDistance = mTouchExplorationTapSlop * COEFFICIENT_DRAGGING_DISTANCE;
+        mPointerTracker = new PointerTracker(context);
+        mContext = context;
+        mHandler = new Handler(context.getMainLooper());
+        mSendHoverDelayed = new SendHoverDelayed();
+        mAccessibilityManager = AccessibilityManager.getInstance(context);
+
+        // Populate the temporary array with PointerCorrds to be reused.
+        for (int i = 0, count = mTempPointerCoords.length; i < count; i++) {
+            mTempPointerCoords[i] = new PointerCoords();
+        }
+    }
+
+    public void clear(MotionEvent event, int policyFlags) {
+        sendUpForInjectedDownPointers(event, policyFlags);
+        clear();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void onMotionEvent(MotionEvent event, int policyFlags) {
+        if (DEBUG) {
+            Slog.d(LOG_TAG_RECEIVED, "Received event: " + event + ", policyFlags=0x"
+                    + Integer.toHexString(policyFlags));
+            Slog.d(LOG_TAG_STATE, sStateSymbolicNames.get(mCurrentState));
+        }
+
+        // Keep track of the pointers's state.
+        mPointerTracker.onReceivedMotionEvent(event);
+
+        switch(mCurrentState) {
+            case STATE_TOUCH_EXPLORING: {
+                handleMotionEventStateTouchExploring(event, policyFlags);
+            } break;
+            case STATE_DRAGGING: {
+                handleMotionEventStateDragging(event, policyFlags);
+            } break;
+            case STATE_DELEGATING: {
+                handleMotionEventStateDelegating(event, policyFlags);
+            } break;
+            default: {
+                throw new IllegalStateException("Illegal state: " + mCurrentState);
+            }
+        }
+    }
+
+    /**
+     * Handles a motion event in touch exploring state.
+     *
+     * @param event The event to be handled.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void handleMotionEventStateTouchExploring(MotionEvent event, int policyFlags) {
+        PointerTracker pointerTracker = mPointerTracker;
+        final int activePointerCount = pointerTracker.getActivePointerCount();
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN: {
+                // Send a hover for every finger down so the user gets feedback
+                // where she is currently touching.
+                mSendHoverDelayed.forceSendAndRemove();
+                mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, 0, policyFlags,
+                        DELAY_SEND_HOVER_MOVE);
+            } break;
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                switch (activePointerCount) {
+                    case 0: {
+                        throw new IllegalStateException("The must always be one active pointer in"
+                                + "touch exploring state!");
+                    }
+                    case 1: {
+                        // Schedule a hover event which will lead to firing an
+                        // accessibility event from the hovered view.
+                        mSendHoverDelayed.remove();
+                        final int pointerId = pointerTracker.getPrimaryActivePointerId();
+                        final int pointerIndex = event.findPointerIndex(pointerId);
+                        final int lastAction = pointerTracker.getLastInjectedHoverAction();
+                        // If a schedules hover enter for another pointer is delivered we send move.
+                        final int action = (lastAction == MotionEvent.ACTION_HOVER_ENTER)
+                                ? MotionEvent.ACTION_HOVER_MOVE
+                                : MotionEvent.ACTION_HOVER_ENTER;
+                        mSendHoverDelayed.post(event, action, pointerIndex, policyFlags,
+                                DELAY_SEND_HOVER_MOVE);
+
+                        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;
+                    default: {
+                        /* do nothing - let the code for ACTION_MOVE decide what to do */
+                    } break;
+                }
+            } break;
+            case MotionEvent.ACTION_MOVE: {
+                switch (activePointerCount) {
+                    case 0: {
+                        /* do nothing - no active pointers so we swallow the event */
+                    } break;
+                    case 1: {
+                        final int pointerId = pointerTracker.getPrimaryActivePointerId();
+                        final int pointerIndex = event.findPointerIndex(pointerId);
+
+                        // Detect touch exploration gesture start by having one active pointer
+                        // that moved more than a given distance.
+                        if (!mTouchExploreGestureInProgress) {
+                            final float deltaX = pointerTracker.getReceivedPointerDownX(pointerId)
+                                - event.getX(pointerIndex);
+                            final float deltaY = pointerTracker.getReceivedPointerDownY(pointerId)
+                                - event.getY(pointerIndex);
+                            final double moveDelta = Math.hypot(deltaX, deltaY);
+
+                            if (moveDelta > mTouchExplorationTapSlop) {
+                                mTouchExploreGestureInProgress = true;
+                                sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
+                                // Make sure the scheduled down/move event is sent.
+                                mSendHoverDelayed.forceSendAndRemove();
+                                sendHoverEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIndex,
+                                        policyFlags);
+                            }
+                        } else {
+                            // Touch exploration gesture in progress so send a hover event.
+                            sendHoverEvent(event,  MotionEvent.ACTION_HOVER_MOVE, pointerIndex,
+                                    policyFlags);
+                        }
+
+                        // Detect long press on the last touch explored position.
+                        if (!mTouchExploreGestureInProgress && mLastTouchExploreEvent != null) {
+
+                            // If the down was not in the time slop => nothing else to do.
+                            final long pointerDownTime =
+                                pointerTracker.getReceivedPointerDownTime(pointerId);
+                            final long lastExploreTime = mLastTouchExploreEvent.getEventTime();
+                            final long deltaTimeExplore = pointerDownTime - lastExploreTime;
+                            if (deltaTimeExplore > ACTIVATION_TIME_SLOP) {
+                                mLastTouchExploreEvent = null;
+                                break;
+                            }
+
+                            // If the pointer moved more than the tap slop => nothing else to do.
+                            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;
+                               break;
+                            }
+
+                            // If down for long enough we get a long press.
+                            final long deltaTimeMove = event.getEventTime() - pointerDownTime;
+                            if (deltaTimeMove > ViewConfiguration.getLongPressTimeout()) {
+                                mCurrentState = STATE_DELEGATING;
+                                // Make sure the scheduled hover exit is delivered.
+                                mSendHoverDelayed.forceSendAndRemove();
+                                sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+                                sendMotionEvent(event, policyFlags);
+                                mTouchExploreGestureInProgress = false;
+                                mLastTouchExploreEvent = null;
+                            }
+                        }
+                    } break;
+                    case 2: {
+                        // Make sure the scheduled hover enter is delivered.
+                        mSendHoverDelayed.forceSendAndRemove();
+                        // We want to no longer hover over the location so subsequent
+                        // touch at the same spot will generate a hover enter.
+                        final int pointerId = pointerTracker.getPrimaryActivePointerId();
+                        final int pointerIndex = event.findPointerIndex(pointerId);
+                        sendHoverEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIndex,
+                                policyFlags);
+
+                        if (isDraggingGesture(event)) {
+                            // Two pointers moving in the same direction within
+                            // a given distance perform a drag.
+                            mCurrentState = STATE_DRAGGING;      
+                            if (mTouchExploreGestureInProgress) {
+                                sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+                                mTouchExploreGestureInProgress = false;
+                            }
+                            mDraggingPointerId = pointerTracker.getPrimaryActivePointerId();
+                            sendDragEvent(event, MotionEvent.ACTION_DOWN, policyFlags);
+                        } else {
+                            // Two pointers moving arbitrary are delegated to the view hierarchy.
+                            mCurrentState = STATE_DELEGATING;
+                            if (mTouchExploreGestureInProgress) {
+                                sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+                                mTouchExploreGestureInProgress = false;
+                            }
+                            sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+                        }
+                    } break;
+                    default: {
+                        // Make sure the scheduled hover enter is delivered.
+                        mSendHoverDelayed.forceSendAndRemove();
+                        // We want to no longer hover over the location so subsequent
+                        // touch at the same spot will generate a hover enter.
+                        final int pointerId = pointerTracker.getPrimaryActivePointerId();
+                        final int pointerIndex = event.findPointerIndex(pointerId);
+                        sendHoverEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIndex,
+                                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;
+                        }
+                        sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+                    }
+                }
+            } break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_POINTER_UP: {
+                switch (activePointerCount) {
+                    case 0: {
+                        // If the pointer that went up was not active we have nothing to do.
+                        if (!pointerTracker.wasLastReceivedUpPointerActive()) {
+                            break;
+                        }
+
+                        // If touch exploring announce the end of the gesture.
+                        if (mTouchExploreGestureInProgress) {
+                            sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+                            mTouchExploreGestureInProgress = false;
+                        }
+
+                        // Detect whether to activate i.e. click on the last explored location.
+                        if (mLastTouchExploreEvent != null) {
+                            final int pointerId = pointerTracker.getLastReceivedUpPointerId();
+
+                            // If the down was not in the time slop => nothing else to do.
+                            final long eventTime =
+                                pointerTracker.getLastReceivedUpPointerDownTime();
+                            final long exploreTime = mLastTouchExploreEvent.getEventTime();
+                            final long deltaTime = eventTime - exploreTime;
+                            if (deltaTime > ACTIVATION_TIME_SLOP) {
+                                mSendHoverDelayed.forceSendAndRemove();
+                                scheduleHoverExit(event, policyFlags);
+                                mLastTouchExploreEvent = MotionEvent.obtain(event);
+                                break;
+                            }
+
+                            // If the pointer moved more than the tap slop => nothing else to do.
+                            final int pointerIndex = event.findPointerIndex(pointerId);
+                            final float deltaX = pointerTracker.getLastReceivedUpPointerDownX()
+                                    - event.getX(pointerIndex);
+                            final float deltaY = pointerTracker.getLastReceivedUpPointerDownY()
+                                    - event.getY(pointerIndex);
+                            final float deltaMove = (float) Math.hypot(deltaX, deltaY);
+                            if (deltaMove > mTouchExplorationTapSlop) {
+                                mSendHoverDelayed.forceSendAndRemove();
+                                scheduleHoverExit(event, policyFlags);
+                                mLastTouchExploreEvent = MotionEvent.obtain(event);
+                                break;
+                            }
+
+                            // All preconditions are met, so click the last explored location.
+                            mSendHoverDelayed.forceSendAndRemove();
+                            sendActionDownAndUp(mLastTouchExploreEvent, policyFlags);
+                            mLastTouchExploreEvent = null;
+                        } else {
+                            mSendHoverDelayed.forceSendAndRemove();
+                            scheduleHoverExit(event, policyFlags);
+                            mLastTouchExploreEvent = MotionEvent.obtain(event);
+                        }
+                    } break;
+                }
+            } break;
+            case MotionEvent.ACTION_CANCEL: {
+                final int lastAction = pointerTracker.getLastInjectedHoverAction();
+                if (lastAction != MotionEvent.ACTION_HOVER_EXIT) {
+                    final int pointerId = pointerTracker.getPrimaryActivePointerId();
+                    final int pointerIndex = event.findPointerIndex(pointerId);
+                    sendHoverEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIndex,
+                            policyFlags);
+                }
+                clear();
+            } break;
+        }
+    }
+
+    /**
+     * Handles a motion event in dragging state.
+     *
+     * @param event The event to be handled.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) {
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN: {
+                throw new IllegalStateException("Dragging state can be reached only if two "
+                        + "pointers are already down");
+            }
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                // We are in dragging state so we have two pointers and another one
+                // goes down => delegate the three pointers to the view hierarchy
+                mCurrentState = STATE_DELEGATING;
+                sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags);
+                sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+            } break;
+            case MotionEvent.ACTION_MOVE: {
+                final int activePointerCount = mPointerTracker.getActivePointerCount();
+                switch (activePointerCount) {
+                    case 2: {
+                        if (isDraggingGesture(event)) {
+                            // If still dragging send a drag event.
+                            sendDragEvent(event, MotionEvent.ACTION_MOVE, policyFlags);
+                        } else {
+                            // The two pointers are moving either in different directions or
+                            // no close enough => delegate the gesture to the view hierarchy.
+                            mCurrentState = STATE_DELEGATING;
+                            // Send an event to the end of the drag gesture.
+                            sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags);
+                            // Deliver all active pointers to the view hierarchy.
+                            sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+                        }
+                    } break;
+                    default: {
+                        mCurrentState = STATE_DELEGATING;
+                        // Send an event to the end of the drag gesture.
+                        sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags);
+                        // Deliver all active pointers to the view hierarchy.
+                        sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+                    }
+                }
+            } break;
+            case MotionEvent.ACTION_POINTER_UP: {
+                mCurrentState = STATE_TOUCH_EXPLORING;
+                // Send an event to the end of the drag gesture.
+                sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags);
+             } break;
+            case MotionEvent.ACTION_CANCEL: {
+                clear();
+            } break;
+        }
+    }
+
+    /**
+     * Handles a motion event in delegating state.
+     *
+     * @param event The event to be handled.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    public void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN: {
+                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
+                final int notInjectedCount = mPointerTracker.getNotInjectedActivePointerCount();
+                if (notInjectedCount > 0) {
+                    sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+                }
+            } break;
+            case MotionEvent.ACTION_POINTER_UP: {
+                // No active pointers => go to initial state.
+                if (mPointerTracker.getActivePointerCount() == 0) {
+                    mCurrentState = STATE_TOUCH_EXPLORING;
+                }
+            } break;
+            case MotionEvent.ACTION_CANCEL: {
+                clear();
+            } break;
+        }
+        // Deliver the event striping out inactive pointers.
+        sendMotionEventStripInactivePointers(event, policyFlags);
+    }
+
+    /**
+     * Schedules a hover up event so subsequent poking on the same location after
+     * the scheduled delay will perform exploration.
+     *
+     * @param prototype The prototype from which to create the injected events.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void scheduleHoverExit(MotionEvent prototype,
+            int policyFlags) {
+        final int pointerId = mPointerTracker.getLastReceivedUpPointerId();
+        final int pointerIndex = prototype.findPointerIndex(pointerId);
+        // We want to no longer hover over the location so subsequent
+        // touch at the same spot will generate a hover enter.
+        mSendHoverDelayed.post(prototype, MotionEvent.ACTION_HOVER_EXIT,
+                 pointerIndex, policyFlags, ACTIVATION_TIME_SLOP);
+    }
+
+    /**
+     * Sends down events to the view hierarchy for all active pointers which are
+     * not already being delivered i.e. pointers that are not yet injected.
+     *
+     * @param prototype The prototype from which to create the injected events.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void sendDownForAllActiveNotInjectedPointers(MotionEvent prototype, int policyFlags) {
+        PointerCoords[] pointerCoords = mTempPointerCoords;
+        PointerTracker pointerTracker = mPointerTracker;
+        int[] pointerIds = mTempPointerIds;
+        int pointerDataIndex = 0;
+
+        final int pinterCount = prototype.getPointerCount();
+        for (int i = 0; i < pinterCount; i++) {
+            final int pointerId = prototype.getPointerId(i);
+
+            // Skip inactive pointers.
+            if (!pointerTracker.isActivePointer(pointerId)) {
+                continue;
+            }
+            // Skip already delivered pointers.
+            if (pointerTracker.isInjectedPointerDown(pointerId)) {
+                continue;
+            }
+
+            // Populate and inject an event for the current pointer.
+            pointerIds[pointerDataIndex] = pointerId;
+            prototype.getPointerCoords(i, pointerCoords[pointerDataIndex]);
+
+            final long downTime = pointerTracker.getLastInjectedDownEventTime();
+            final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, pointerDataIndex);
+            final int pointerCount = pointerDataIndex + 1;
+            final long pointerDownTime = SystemClock.uptimeMillis();
+
+            MotionEvent event = MotionEvent.obtain(downTime, pointerDownTime,
+                    action, pointerCount, pointerIds, pointerCoords, prototype.getMetaState(),
+                    prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(),
+                    prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags());
+            sendMotionEvent(event, policyFlags);
+            event.recycle();
+
+            pointerDataIndex++;
+        }
+    }
+
+    /**
+     * Sends up events to the view hierarchy for all active pointers which are
+     * already being delivered i.e. pointers that are injected.
+     *
+     * @param prototype The prototype from which to create the injected events.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
+        PointerTracker pointerTracker = mPointerTracker;
+        PointerCoords[] pointerCoords = mTempPointerCoords;
+        int[] pointerIds = mTempPointerIds;
+        int pointerDataIndex = 0;
+
+        final int pointerCount = prototype.getPointerCount(); 
+        for (int i = 0; i < pointerCount; i++) {
+            final int pointerId = prototype.getPointerId(i);
+
+            // Skip non injected down pointers.
+            if (!pointerTracker.isInjectedPointerDown(pointerId)) {
+                continue;
+            }
+
+            // Populate and inject event.
+            pointerIds[pointerDataIndex] = pointerId;
+            prototype.getPointerCoords(i, pointerCoords[pointerDataIndex]);
+
+            final long downTime = pointerTracker.getLastInjectedDownEventTime();
+            final int action = computeInjectionAction(MotionEvent.ACTION_UP, pointerDataIndex);
+            final int newPointerCount = pointerDataIndex + 1;
+            final long eventTime = SystemClock.uptimeMillis();
+
+            MotionEvent event = MotionEvent.obtain(downTime, eventTime, action,
+                    newPointerCount, pointerIds, pointerCoords, prototype.getMetaState(),
+                    prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(),
+                    prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags());
+
+            sendMotionEvent(event, policyFlags);
+            event.recycle();
+
+            pointerDataIndex++;
+        }
+    }
+
+    /**
+     * Sends a motion event by first stripping the inactive pointers.
+     *
+     * @param prototype The prototype from which to create the injected event.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void sendMotionEventStripInactivePointers(MotionEvent prototype, int policyFlags) {
+        PointerTracker pointerTracker = mPointerTracker;
+
+        // All pointers active therefore we just inject the event as is.
+        if (prototype.getPointerCount() == pointerTracker.getActivePointerCount()) {
+            sendMotionEvent(prototype, policyFlags);
+            return;
+        }
+
+        // No active pointers and the one that just went up was not
+        // active, therefore we have nothing to do.
+        if (pointerTracker.getActivePointerCount() == 0
+                && !pointerTracker.wasLastReceivedUpPointerActive()) {
+            return;
+        }
+
+        // Filter out inactive pointers from the event and inject it.
+        PointerCoords[] pointerCoords = mTempPointerCoords;
+        int[] pointerIds = mTempPointerIds;
+        int [] newToOldPointerIndexMap = mTempNewToOldPointerIndexMap;
+        int newPointerIndex = 0;
+        int actionIndex = prototype.getActionIndex();
+
+        final int oldPointerCount = prototype.getPointerCount();
+        for (int oldPointerIndex = 0; oldPointerIndex < oldPointerCount; oldPointerIndex++) {
+            final int pointerId = prototype.getPointerId(oldPointerIndex);
+
+            // If the pointer is inactive or the pointer that just went up
+            // was inactive we strip the pointer data from the event.
+            if (!pointerTracker.isActiveOrWasLastActiveUpPointer(pointerId)) {
+                if (oldPointerIndex <= prototype.getActionIndex()) {
+                    actionIndex--;
+                }
+                continue;
+            }
+
+            newToOldPointerIndexMap[newPointerIndex] = oldPointerIndex;
+            pointerIds[newPointerIndex] = pointerId;
+            prototype.getPointerCoords(oldPointerIndex, pointerCoords[newPointerIndex]);
+
+            newPointerIndex++;
+        }
+
+        // If we skipped all pointers => nothing to do.
+        if (newPointerIndex == 0) {
+            return;
+        }
+
+        // Populate and inject the event.
+        final long downTime = pointerTracker.getLastInjectedDownEventTime();
+        final int action = computeInjectionAction(prototype.getActionMasked(), actionIndex);
+        final int newPointerCount = newPointerIndex;
+        MotionEvent prunedEvent = MotionEvent.obtain(downTime, prototype.getEventTime(), action,
+                newPointerCount, pointerIds, pointerCoords, prototype.getMetaState(),
+                prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(),
+                prototype.getEdgeFlags(), prototype.getSource(),prototype.getFlags());
+
+        // Add the filtered history.
+        final int historySize = prototype.getHistorySize();
+        for (int historyIndex = 0; historyIndex < historySize; historyIndex++) {
+            for (int pointerIndex = 0; pointerIndex < newPointerCount; pointerIndex++) {
+                final int oldPointerIndex = newToOldPointerIndexMap[pointerIndex];
+                prototype.getPointerCoords(oldPointerIndex, pointerCoords[pointerIndex]);
+            }
+            final long historicalTime = prototype.getHistoricalEventTime(historyIndex);
+            prunedEvent.addBatch(historicalTime, pointerCoords, 0);
+        }
+
+        sendMotionEvent(prunedEvent, policyFlags);
+        prunedEvent.recycle();
+    }
+
+    /**
+     * Sends a dragging event from a two pointer event. The two pointers are
+     * merged into one and delivered to the view hierarchy. Through the entire
+     * drag gesture the pointer id delivered to the view hierarchy is the same.
+     *
+     * @param prototype The prototype from which to create the injected event.
+     * @param action The dragging action that is to be injected.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void sendDragEvent(MotionEvent prototype, int action, int policyFlags) {
+        PointerCoords[] pointerCoords = mTempPointerCoords;
+        int[] pointerIds = mTempPointerIds;
+        final int pointerId = mDraggingPointerId;
+        final int pointerIndex = prototype.findPointerIndex(pointerId);
+
+        // Populate the event with the date of the dragging pointer and inject it.
+        pointerIds[0] = pointerId;
+        prototype.getPointerCoords(pointerIndex, pointerCoords[0]);
+
+        MotionEvent event = MotionEvent.obtain(prototype.getDownTime(),
+                prototype.getEventTime(), action, 1, pointerIds, pointerCoords,
+                prototype.getMetaState(), prototype.getXPrecision(), prototype.getYPrecision(),
+                prototype.getDeviceId(), prototype.getEdgeFlags(), prototype.getSource(),
+                prototype.getFlags());
+
+        sendMotionEvent(event, policyFlags);
+        event.recycle();
+    }
+
+    /**
+     * Sends an up and down events.
+     *
+     * @param prototype The prototype from which to create the injected events.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) {
+        PointerCoords[] pointerCoords = mTempPointerCoords;
+        int[] pointerIds = mTempPointerIds;
+        final int pointerId = mPointerTracker.getLastReceivedUpPointerId();
+        final int pointerIndex = prototype.findPointerIndex(pointerId);
+
+        // Send down.
+        pointerIds[0] = pointerId;
+        prototype.getPointerCoords(pointerIndex, pointerCoords[0]);
+
+        final long downTime = SystemClock.uptimeMillis();
+
+        MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
+                1, mTempPointerIds, mTempPointerCoords, prototype.getMetaState(),
+                prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(),
+                prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags());
+
+        // Clone the down event before recycling it.
+        MotionEvent upEvent = MotionEvent.obtain(downEvent);
+
+        sendMotionEvent(downEvent, policyFlags);
+        downEvent.recycle();
+
+        // Send up.
+        upEvent.setAction(MotionEvent.ACTION_UP);
+        sendMotionEvent(upEvent, policyFlags);
+        upEvent.recycle();
+    }
+
+    /**
+     * Sends a hover event.
+     *
+     * @param prototype The prototype from which to create the injected event.
+     * @param action The hover action.
+     * @param pointerIndex The action pointer index.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void sendHoverEvent(MotionEvent prototype, int action, int pointerIndex, int
+            policyFlags) {
+        PointerCoords[] pointerCoords = mTempPointerCoords;
+        int[] pointerIds = mTempPointerIds;
+
+        // Keep only data relevant to a hover event.
+        pointerIds[0] = prototype.getPointerId(pointerIndex);
+        pointerCoords[0].clear();
+        pointerCoords[0].x = prototype.getX(pointerIndex);
+        pointerCoords[0].y = prototype.getY(pointerIndex);
+
+        final long downTime = mPointerTracker.getLastInjectedDownEventTime();
+
+        // Populate and inject a hover event.
+        MotionEvent hoverEvent = MotionEvent.obtain(downTime, prototype.getEventTime(), action,
+                1, pointerIds, pointerCoords, 0, 0, 0, prototype.getDeviceId(), 0,
+                prototype.getSource(), 0);
+
+        sendMotionEvent(hoverEvent, policyFlags);
+        hoverEvent.recycle();
+    }
+
+    /**
+     * Computes the action for an injected event based on a masked action
+     * and a pointer index.
+     *
+     * @param actionMasked The masked action.
+     * @param pointerIndex The index of the pointer which has changed.
+     * @return The action to be used for injection.
+     */
+    private int computeInjectionAction(int actionMasked, int pointerIndex) {
+        switch (actionMasked) {
+            case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                PointerTracker pointerTracker = mPointerTracker;
+                // Compute the action based on how many down pointers are injected.
+                if (pointerTracker.getInjectedPointerDownCount() == 0) {
+                    return MotionEvent.ACTION_DOWN;
+                } else {
+                    return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
+                        | MotionEvent.ACTION_POINTER_DOWN;
+                }
+            }
+            case MotionEvent.ACTION_POINTER_UP: {
+                PointerTracker pointerTracker = mPointerTracker;
+                // Compute the action based on how many down pointers are injected.
+                if (pointerTracker.getInjectedPointerDownCount() == 1) {
+                    return MotionEvent.ACTION_UP;
+                } else {
+                    return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
+                        | MotionEvent.ACTION_POINTER_UP;
+                }
+            }
+            default:
+                return actionMasked;
+        }
+    }
+
+    /**
+     * Determines whether a two pointer gesture is a dragging one.
+     *
+     * @param event The event with the pointer data.
+     * @return True if the gesture is a dragging one.
+     */
+    private boolean isDraggingGesture(MotionEvent event) {
+        PointerTracker pointerTracker = mPointerTracker;
+        int[] pointerIds = mTempPointerIds;
+        pointerTracker.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);
+
+        // Check if the pointers are close enough.
+        final float deltaX = firstPtrX - secondPtrX;
+        final float deltaY = firstPtrY - secondPtrY;
+        final float deltaMove = (float) Math.hypot(deltaX, deltaY);
+        if (deltaMove > mDraggingDistance) {
+            return false;
+        }
+
+        // Check if the pointers are moving in the same direction.
+        final float firstDeltaX =
+            firstPtrX - pointerTracker.getReceivedPointerDownX(firstPtrIndex);
+        final float firstDeltaY =
+            firstPtrY - pointerTracker.getReceivedPointerDownY(firstPtrIndex);
+        final float firstMagnitude =
+            (float) Math.sqrt(firstDeltaX * firstDeltaX + firstDeltaY * firstDeltaY);
+        final float firstXNormalized =
+            (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX;
+        final float firstYNormalized =
+            (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
+
+        final float secondDeltaX =
+            secondPtrX - pointerTracker.getReceivedPointerDownX(secondPtrIndex);
+        final float secondDeltaY =
+            secondPtrY - pointerTracker.getReceivedPointerDownY(secondPtrIndex);
+        final float secondMagnitude =
+            (float) Math.sqrt(secondDeltaX * secondDeltaX + secondDeltaY * secondDeltaY);
+        final float secondXNormalized =
+            (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX;
+        final float secondYNormalized =
+            (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY;
+
+        final float angleCos =
+            firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized;
+
+        if (angleCos < MIN_ANGLE_COS) {
+            return false;
+        }
+
+        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);
+        event.setPackageName(mContext.getPackageName());
+        event.setClassName(getClass().getName());
+        mAccessibilityManager.sendAccessibilityEvent(event);
+    }
+
+    /**
+     * Sends a motion event to the input filter for injection.
+     *
+     * @param event The event to send.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void sendMotionEvent(MotionEvent event, int policyFlags) {
+        if (DEBUG) {
+            Slog.d(LOG_TAG_INJECTED, "Injecting event: " + event + ", policyFlags=0x"
+                    + Integer.toHexString(policyFlags));
+        }
+        // Make sure that the user will see the event.
+        policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
+        mPointerTracker.onInjectedMotionEvent(event);
+        mInputFilter.sendInputEvent(event, policyFlags);
+    }
+
+    /**
+     * Clears the internal state of this explorer.
+     */
+    private void clear() {
+        mSendHoverDelayed.remove();
+        mPointerTracker.clear();
+        mLastTouchExploreEvent = null;
+        mCurrentState = STATE_TOUCH_EXPLORING;
+        mTouchExploreGestureInProgress = false;
+        mDraggingPointerId = INVALID_POINTER_ID;
+    }
+
+    /**
+     * Helper class for tracking pointers and more specifically which of
+     * them are currently down, which are active, and which are delivered
+     * to the view hierarchy. The enclosing {@link TouchExplorer} uses the
+     * pointer state reported by this class to perform touch exploration.
+     * <p>
+     * The main purpose of this class is to allow the touch explorer to
+     * disregard pointers put down by accident by the user and not being
+     * involved in the interaction. For example, a blind user grabs the
+     * device with her left hand such that she touches the screen and she
+     * uses her right hand's index finger to explore the screen content.
+     * In this scenario the touches generated by the left hand are to be
+     * ignored.
+     */
+    class PointerTracker {
+        private static final String LOG_TAG = "PointerTracker";
+
+        // The coefficient by which to multiply
+        // ViewConfiguration.#getScaledTouchSlop()
+        // to compute #mThresholdActivePointer.
+        private static final int COEFFICIENT_ACTIVE_POINTER = 2;
+
+        // Pointers that moved less than mThresholdActivePointer
+        // are considered active i.e. are ignored.
+        private final double mThresholdActivePointer;
+
+        // Keep track of where and when a pointer went down.
+        private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT];
+        private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT];
+        private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT];
+
+        // Which pointers are down.
+        private int mReceivedPointersDown;
+
+        // Which down pointers are active.
+        private int mActivePointers;
+
+        // Primary active pointer which is either the first that went down
+        // or if it goes up the next active that most recently went down.
+        private int mPrimaryActivePointerId;
+
+        // Flag indicating that there is at least one active pointer moving.
+        private boolean mHasMovingActivePointer;
+
+        // Keep track of which pointers sent to the system are down.
+        private int mInjectedPointersDown;
+
+        // Keep track of the last up pointer data.
+        private float mLastReceivedUpPointerDownX;
+        private float mLastReveivedUpPointerDownY;
+        private long mLastReceivedUpPointerDownTime;
+        private int mLastReceivedUpPointerId;
+        private boolean mLastReceivedUpPointerActive;
+
+        // 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;
+
+        /**
+         * Creates a new instance.
+         *
+         * @param context Context for looking up resources.
+         */
+        public PointerTracker(Context context) {
+            mThresholdActivePointer =
+                ViewConfiguration.get(context).getScaledTouchSlop() * COEFFICIENT_ACTIVE_POINTER;
+        }
+
+        /**
+         * Clears the internals state.
+         */
+        public void clear() {
+            Arrays.fill(mReceivedPointerDownX, 0);
+            Arrays.fill(mReceivedPointerDownY, 0);
+            Arrays.fill(mReceivedPointerDownTime, 0);
+            mReceivedPointersDown = 0;
+            mActivePointers = 0;
+            mPrimaryActivePointerId = 0;
+            mHasMovingActivePointer = false;
+            mInjectedPointersDown = 0;
+            mLastReceivedUpPointerDownX = 0;
+            mLastReveivedUpPointerDownY = 0;
+            mLastReceivedUpPointerDownTime = 0;
+            mLastReceivedUpPointerId = 0;
+            mLastReceivedUpPointerActive = false;
+        }
+
+        /**
+         * Processes a received {@link MotionEvent} event.
+         *
+         * @param event The event to process.
+         */
+        public void onReceivedMotionEvent(MotionEvent event) {
+            final int action = event.getActionMasked();
+            switch (action) {
+                case MotionEvent.ACTION_DOWN: {
+                    // New gesture so restart tracking injected down pointers.
+                    mInjectedPointersDown = 0;
+                    handleReceivedPointerDown(0, event);
+                } break;
+                case MotionEvent.ACTION_POINTER_DOWN: {
+                    handleReceivedPointerDown(event.getActionIndex(), event);
+                } break;
+                case MotionEvent.ACTION_MOVE: {
+                    handleReceivedPointerMove(event);
+                } break;
+                case MotionEvent.ACTION_UP: {
+                    handleReceivedPointerUp(0, event);
+                } break;
+                case MotionEvent.ACTION_POINTER_UP: {
+                    handleReceivedPointerUp(event.getActionIndex(), event);
+                } break;
+            }
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Received pointer: " + toString());
+            }
+        }
+
+        /**
+         * Processes an injected {@link MotionEvent} event.
+         *
+         * @param event The event to process.
+         */
+        public void onInjectedMotionEvent(MotionEvent event) {
+            final int action = event.getActionMasked();
+            switch (action) {
+                case MotionEvent.ACTION_DOWN: {
+                    handleInjectedPointerDown(0, event);
+                } break;
+                case MotionEvent.ACTION_POINTER_DOWN: {
+                    handleInjectedPointerDown(event.getActionIndex(), event);
+                } break;
+                case MotionEvent.ACTION_UP: {
+                    handleInjectedPointerUp(0, event);
+                } break;
+                case MotionEvent.ACTION_POINTER_UP: {
+                    handleInjectedPointerUp(event.getActionIndex(), event);
+                } break;
+                case MotionEvent.ACTION_HOVER_ENTER:
+                case MotionEvent.ACTION_HOVER_MOVE:
+                case MotionEvent.ACTION_HOVER_EXIT: {
+                    mLastInjectedHoverEventAction = event.getActionMasked();
+                } break;
+            }
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Injected pointer: " + toString());
+            }
+        }
+
+        /**
+         * @return The number of received pointers that are down.
+         */
+        public int getReceivedPointerDownCount() {
+            return Integer.bitCount(mReceivedPointersDown);
+        }
+
+        /**
+         * @return The number of down input  pointers that are active.
+         */
+        public int getActivePointerCount() {
+            return Integer.bitCount(mActivePointers);
+        }
+
+        /**
+         * Whether an received pointer is down.
+         *
+         * @param pointerId The unique pointer id.
+         * @return True if the pointer is down.
+         */
+        public boolean isReceivedPointerDown(int pointerId) {
+            final int pointerFlag = (1 << pointerId);
+            return (mReceivedPointersDown & pointerFlag) != 0;
+        }
+
+        /**
+         * Whether an injected pointer is down.
+         *
+         * @param pointerId The unique pointer id.
+         * @return True if the pointer is down.
+         */
+        public boolean isInjectedPointerDown(int pointerId) {
+            final int pointerFlag = (1 << pointerId);
+            return (mInjectedPointersDown & pointerFlag) != 0;
+        }
+
+        /**
+         * @return The number of down pointers injected to the view hierarchy.
+         */
+        public int getInjectedPointerDownCount() {
+            return Integer.bitCount(mInjectedPointersDown);
+        }
+
+        /**
+         * Whether an input pointer is active.
+         *
+         * @param pointerId The unique pointer id.
+         * @return True if the pointer is active.
+         */
+        public boolean isActivePointer(int pointerId) {
+            final int pointerFlag = (1 << pointerId);
+            return (mActivePointers & pointerFlag) != 0;
+        }
+
+        /**
+         * @param pointerId The unique pointer id.
+         * @return The X coordinate where the pointer went down.
+         */
+        public float getReceivedPointerDownX(int pointerId) {
+            return mReceivedPointerDownX[pointerId];
+        }
+
+        /**
+         * @param pointerId The unique pointer id.
+         * @return The Y coordinate where the pointer went down.
+         */
+        public float getReceivedPointerDownY(int pointerId) {
+            return mReceivedPointerDownY[pointerId];
+        }
+
+        /**
+         * @param pointerId The unique pointer id.
+         * @return The time when the pointer went down.
+         */
+        public long getReceivedPointerDownTime(int pointerId) {
+            return mReceivedPointerDownTime[pointerId];
+        }
+
+        /**
+         * @return The id of the primary pointer.
+         */
+        public int getPrimaryActivePointerId() {
+            if (mPrimaryActivePointerId == INVALID_POINTER_ID) {
+                mPrimaryActivePointerId = findPrimaryActivePointer();
+            }
+            return mPrimaryActivePointerId;
+        }
+
+        /**
+         * @return The X coordinate where the last up received pointer went down.
+         */
+        public float getLastReceivedUpPointerDownX() {
+            return mLastReceivedUpPointerDownX;
+        }
+
+        /**
+         * @return The Y coordinate where the last up received pointer went down.
+         */
+        public float getLastReceivedUpPointerDownY() {
+            return mLastReveivedUpPointerDownY;
+        }
+
+        /**
+         * @return The time when the last up received pointer went down.
+         */
+        public long getLastReceivedUpPointerDownTime() {
+            return mLastReceivedUpPointerDownTime;
+        }
+
+        /**
+         * @return The id of the last received pointer that went up.
+         */
+        public int getLastReceivedUpPointerId() {
+            return mLastReceivedUpPointerId;
+        }
+
+        /**
+         * @return Whether the last received pointer that went up was active.
+         */
+        public boolean wasLastReceivedUpPointerActive() {
+            return mLastReceivedUpPointerActive;
+        }
+
+        /**
+         * @return The time of the last injected down event.
+         */
+        public long getLastInjectedDownEventTime() {
+            return mLastInjectedDownEventTime;
+        }
+
+        /**
+         * @return The action of the last injected hover event.
+         */
+        public int getLastInjectedHoverAction() {
+            return mLastInjectedHoverEventAction;
+        }
+
+        /**
+         * Populates the active pointer IDs to the given array.
+         * <p>
+         * Note: The client is responsible for providing large enough array.
+         *
+         * @param outPointerIds The array to which to write the active pointers.
+         */
+        public void populateActivePointerIds(int[] outPointerIds) {
+            int index = 0;
+            for (int idBits = mActivePointers; idBits != 0; ) {
+                final int id = Integer.numberOfTrailingZeros(idBits);
+                idBits &= ~(1 << id);
+                outPointerIds[index] = id;
+                index++;
+            }
+        }
+
+        /**
+         * @return The number of non injected active pointers.
+         */
+        public int getNotInjectedActivePointerCount() {
+            final int pointerState = mActivePointers & ~mInjectedPointersDown;
+            return Integer.bitCount(pointerState);
+        }
+
+        /**
+         * @param pointerId The unique pointer id.
+         * @return Whether the pointer is active or was the last active than went up.
+         */
+        private boolean isActiveOrWasLastActiveUpPointer(int pointerId) {
+            return (isActivePointer(pointerId)
+                    || (mLastReceivedUpPointerId == pointerId
+                            && mLastReceivedUpPointerActive));
+        }
+
+        /**
+         * Handles a received pointer down event.
+         *
+         * @param pointerIndex The index of the pointer that has changed.
+         * @param event The event to be handled.
+         */
+        private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) {
+            final int pointerId = event.getPointerId(pointerIndex);
+            final int pointerFlag = (1 << pointerId);
+
+            mLastReceivedUpPointerId = 0;
+            mLastReceivedUpPointerDownX = 0;
+            mLastReveivedUpPointerDownY = 0;
+            mLastReceivedUpPointerDownTime = 0;
+            mLastReceivedUpPointerActive = false;
+
+            mReceivedPointersDown |= pointerFlag;
+            mReceivedPointerDownX[pointerId] = event.getX(pointerIndex);
+            mReceivedPointerDownY[pointerId] = event.getY(pointerIndex);
+            mReceivedPointerDownTime[pointerId] = event.getEventTime();
+
+            if (!mHasMovingActivePointer) {
+                // If still no moving active pointers every
+                // down pointer is the only active one.
+                mActivePointers = pointerFlag;
+                mPrimaryActivePointerId = pointerId;
+            } else {
+                // If at least one moving active pointer every
+                // subsequent down pointer is active.
+                mActivePointers |= pointerFlag;
+            }
+        }
+
+        /**
+         * Handles a received pointer move event.
+         *
+         * @param event The event to be handled.
+         */
+        private void handleReceivedPointerMove(MotionEvent event) {
+            detectActivePointers(event);
+        }
+
+        /**
+         * Handles a received pointer up event.
+         *
+         * @param pointerIndex The index of the pointer that has changed.
+         * @param event The event to be handled.
+         */
+        private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) {
+            final int pointerId = event.getPointerId(pointerIndex);
+            final int pointerFlag = (1 << pointerId);
+
+            mLastReceivedUpPointerId = pointerId;
+            mLastReceivedUpPointerDownX = getReceivedPointerDownX(pointerId);
+            mLastReveivedUpPointerDownY = getReceivedPointerDownY(pointerId);
+            mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId);
+            mLastReceivedUpPointerActive = isActivePointer(pointerId);
+
+            mReceivedPointersDown &= ~pointerFlag;
+            mActivePointers &= ~pointerFlag;
+            mReceivedPointerDownX[pointerId] = 0;
+            mReceivedPointerDownY[pointerId] = 0;
+            mReceivedPointerDownTime[pointerId] = 0;
+
+            if (mActivePointers == 0) {
+                mHasMovingActivePointer = false;
+            }
+            if (mPrimaryActivePointerId == pointerId) {
+                mPrimaryActivePointerId = INVALID_POINTER_ID;
+            }
+        }
+
+        /**
+         * Handles a injected pointer down event.
+         *
+         * @param pointerIndex The index of the pointer that has changed.
+         * @param event The event to be handled.
+         */
+        private void handleInjectedPointerDown(int pointerIndex, MotionEvent event) {
+            final int pointerId = event.getPointerId(pointerIndex);
+            final int pointerFlag = (1 << pointerId);
+            mInjectedPointersDown |= pointerFlag;
+            mLastInjectedDownEventTime = event.getEventTime();
+        }
+
+        /**
+         * Handles a injected pointer up event.
+         *
+         * @param pointerIndex The index of the pointer that has changed.
+         * @param event The event to be handled.
+         */
+        private void handleInjectedPointerUp(int pointerIndex, MotionEvent event) {
+            final int pointerId = event.getPointerId(pointerIndex);
+            final int pointerFlag = (1 << pointerId);
+            mInjectedPointersDown &= ~pointerFlag;
+        }
+
+        /**
+         * Detects the active pointers in an event.
+         *
+         * @param event The event to examine.
+         */
+        private void detectActivePointers(MotionEvent event) {
+            for (int i = 0, count = event.getPointerCount(); i < count; i++) {
+                final int pointerId = event.getPointerId(i);
+                if (mHasMovingActivePointer) {
+                    // If already active => nothing to do.
+                    if (isActivePointer(pointerId)) {
+                        continue;
+                    }
+                }
+                // Active pointers are ones that moved more than a given threshold.
+                final float pointerDeltaMove = computePointerDeltaMove(i, event);
+                if (pointerDeltaMove > mThresholdActivePointer) {
+                    final int pointerFlag = (1 << pointerId);
+                    mActivePointers |= pointerFlag;
+                    mHasMovingActivePointer = true;
+                }
+            }
+        }
+
+        /**
+         * @return The primary active pointer.
+         */
+        private int findPrimaryActivePointer() {
+            int primaryActivePointerId = INVALID_POINTER_ID;
+            long minDownTime = Long.MAX_VALUE;
+            // Find the active pointer that went down first.
+            for (int i = 0, count = mReceivedPointerDownTime.length; i < count; i++) {
+                if (isActivePointer(i)) {
+                    final long downPointerTime = mReceivedPointerDownTime[i];
+                    if (downPointerTime < minDownTime) {
+                        minDownTime = downPointerTime;
+                        primaryActivePointerId = i;
+                    }
+                }
+            }
+            return primaryActivePointerId;
+        }
+
+        /**
+         * Computes the move for a given action pointer index since the
+         * corresponding pointer went down.
+         *
+         * @param pointerIndex The action pointer index.
+         * @param event The event to examine.
+         * @return The distance the pointer has moved.
+         */
+        private float computePointerDeltaMove(int pointerIndex, MotionEvent event) {
+            final int pointerId = event.getPointerId(pointerIndex);
+            final float deltaX = event.getX(pointerIndex) - mReceivedPointerDownX[pointerId];
+            final float deltaY = event.getY(pointerIndex) - mReceivedPointerDownY[pointerId];
+            return (float) Math.hypot(deltaX, deltaY);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("=========================");
+            builder.append("\nDown pointers #");
+            builder.append(getReceivedPointerDownCount());
+            builder.append(" [ ");
+            for (int i = 0; i < MAX_POINTER_COUNT; i++) {
+                if (isReceivedPointerDown(i)) {
+                    builder.append(i);
+                    builder.append(" ");
+                }
+            }
+            builder.append("]");
+            builder.append("\nActive pointers #");
+            builder.append(getActivePointerCount());
+            builder.append(" [ ");
+            for (int i = 0; i < MAX_POINTER_COUNT; i++) {
+                if (isActivePointer(i)) {
+                    builder.append(i);
+                    builder.append(" ");
+                }
+            }
+            builder.append("]");
+            builder.append("\nPrimary active pointer id [ ");
+            builder.append(getPrimaryActivePointerId());
+            builder.append(" ]");
+            builder.append("\n=========================");
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Class for delayed sending of hover events.
+     */
+    private final class SendHoverDelayed implements Runnable {
+        private static final String LOG_TAG = "SendHoverEnterOrExitDelayed";
+
+        private MotionEvent mEvent;
+        private int mAction;
+        private int mPointerIndex;
+        private int mPolicyFlags;
+
+        public void post(MotionEvent prototype, int action, int pointerIndex, int policyFlags,
+                long delay) {
+            remove();
+            mEvent = MotionEvent.obtain(prototype);
+            mAction = action;
+            mPointerIndex = pointerIndex;
+            mPolicyFlags = policyFlags;
+            mHandler.postDelayed(this, delay);
+        }
+
+        public void remove() {
+            mHandler.removeCallbacks(this);
+            clear();
+        }
+
+        private boolean isPenidng() {
+            return (mEvent != null);
+        }
+
+        private void clear() {
+            if (!isPenidng()) {
+                return;
+            }
+            mEvent.recycle();
+            mEvent = null;
+            mAction = 0;
+            mPointerIndex = -1;
+            mPolicyFlags = 0;
+        }
+
+        public void forceSendAndRemove() {
+            if (isPenidng()) {
+                run();
+                remove();
+            }
+        }
+
+        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");
+                }
+            }
+
+            sendHoverEvent(mEvent, mAction, mPointerIndex, mPolicyFlags);
+            clear();
+        }
+    }
+}
diff --git a/services/java/com/android/server/wm/InputFilter.java b/services/java/com/android/server/wm/InputFilter.java
index 7e1ab07..8f0001a 100644
--- a/services/java/com/android/server/wm/InputFilter.java
+++ b/services/java/com/android/server/wm/InputFilter.java
@@ -105,11 +105,13 @@
     private final InputEventConsistencyVerifier mInboundInputEventConsistencyVerifier =
             InputEventConsistencyVerifier.isInstrumentationEnabled() ?
                     new InputEventConsistencyVerifier(this,
-                            InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT) : null;
+                            InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT,
+                            "InputFilter#InboundInputEventConsistencyVerifier") : null;
     private final InputEventConsistencyVerifier mOutboundInputEventConsistencyVerifier =
             InputEventConsistencyVerifier.isInstrumentationEnabled() ?
                     new InputEventConsistencyVerifier(this,
-                            InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT) : null;
+                            InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT,
+                            "InputFilter#OutboundInputEventConsistencyVerifier") : null;
 
     /**
      * Creates the input filter.
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 538e38c..8b739a4 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -398,6 +398,7 @@
     int mRotation = 0;
     int mRequestedRotation = 0;
     int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    boolean mAltOrientation = false;
     int mLastRotationFlags;
     ArrayList<IRotationWatcher> mRotationWatchers
             = new ArrayList<IRotationWatcher>();
@@ -585,7 +586,6 @@
     }
 
     final Configuration mTempConfiguration = new Configuration();
-    int mScreenLayout = Configuration.SCREENLAYOUT_SIZE_UNDEFINED;
 
     // The frame use to limit the size of the app running in compatibility mode.
     Rect mCompatibleScreenFrame = new Rect();
@@ -4954,7 +4954,52 @@
         rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation,
                 mRotation, mDisplayEnabled);
         if (DEBUG_ORIENTATION) Slog.v(TAG, "new rotation is set to " + rotation);
+
+        int desiredRotation = rotation;
+        int lockedRotation = mPolicy.getLockedRotationLw();
+        if (lockedRotation >= 0 && rotation != lockedRotation) {
+            // We are locked in a rotation but something is requesting
+            // a different rotation...  we will either keep the locked
+            // rotation if it results in the same orientation, or have to
+            // switch into an emulated orientation mode.
+
+            // First, we know that our rotation is actually going to be
+            // the locked rotation.
+            rotation = lockedRotation;
+
+            // Now the difference between the desired and lockedRotation
+            // may mean that the orientation is different...  if that is
+            // not the case, we can just make the desired rotation be the
+            // same as the new locked rotation.
+            switch (lockedRotation) {
+                case Surface.ROTATION_0:
+                    if (rotation == Surface.ROTATION_180) {
+                        desiredRotation = lockedRotation;
+                    }
+                    break;
+                case Surface.ROTATION_90:
+                    if (rotation == Surface.ROTATION_270) {
+                        desiredRotation = lockedRotation;
+                    }
+                    break;
+                case Surface.ROTATION_180:
+                    if (rotation == Surface.ROTATION_0) {
+                        desiredRotation = lockedRotation;
+                    }
+                    break;
+                case Surface.ROTATION_270:
+                    if (rotation == Surface.ROTATION_90) {
+                        desiredRotation = lockedRotation;
+                    }
+                    break;
+            }
+        }
+
         changed = mDisplayEnabled && mRotation != rotation;
+        if (mAltOrientation != (rotation != desiredRotation)) {
+            changed = true;
+            mAltOrientation = rotation != desiredRotation;
+        }
 
         if (changed) {
             if (DEBUG_ORIENTATION) Slog.v(TAG,
@@ -5000,6 +5045,7 @@
                     Surface.setOrientation(0, rotation, animFlags);
                 }
             }
+
             for (int i=mWindows.size()-1; i>=0; i--) {
                 WindowState w = mWindows.get(i);
                 if (w.mSurface != null) {
@@ -5440,8 +5486,32 @@
         // Use the effective "visual" dimensions based on current rotation
         final boolean rotated = (mRotation == Surface.ROTATION_90
                 || mRotation == Surface.ROTATION_270);
-        final int dw = rotated ? mInitialDisplayHeight : mInitialDisplayWidth;
-        final int dh = rotated ? mInitialDisplayWidth : mInitialDisplayHeight;
+        final int realdw = rotated ? mInitialDisplayHeight : mInitialDisplayWidth;
+        final int realdh = rotated ? mInitialDisplayWidth : mInitialDisplayHeight;
+
+        if (mAltOrientation) {
+            mCurDisplayWidth = realdw;
+            mCurDisplayHeight = realdh;
+            if (realdw > realdh) {
+                // Turn landscape into portrait.
+                int maxw = (int)(realdh/1.3f);
+                if (maxw < realdw) {
+                    mCurDisplayWidth = maxw;
+                }
+            } else {
+                // Turn portrait into landscape.
+                int maxh = (int)(realdw/1.3f);
+                if (maxh < realdh) {
+                    mCurDisplayHeight = maxh;
+                }
+            }
+        } else {
+            mCurDisplayWidth = realdw;
+            mCurDisplayHeight = realdh;
+        }
+
+        final int dw = mCurDisplayWidth;
+        final int dh = mCurDisplayHeight;
 
         int orientation = Configuration.ORIENTATION_SQUARE;
         if (dw < dh) {
@@ -5452,66 +5522,69 @@
         config.orientation = orientation;
 
         DisplayMetrics dm = new DisplayMetrics();
-        mDisplay.getMetrics(dm);
+        mDisplay.getRealMetrics(dm);
+
+        // Override display width and height with what we are computing,
+        // to be sure they remain consistent.
+        dm.widthPixels = dw;
+        dm.heightPixels = dh;
+
         CompatibilityInfo.updateCompatibleScreenFrame(dm, orientation, mCompatibleScreenFrame);
 
         config.screenWidthDp = (int)(dm.widthPixels / dm.density);
         config.screenHeightDp = (int)(dm.heightPixels / dm.density);
 
-        if (mScreenLayout == Configuration.SCREENLAYOUT_SIZE_UNDEFINED) {
-            // Note we only do this once because at this point we don't
-            // expect the screen to change in this way at runtime, and want
-            // to avoid all of this computation for every config change.
-            int longSize = dw;
-            int shortSize = dh;
-            if (longSize < shortSize) {
-                int tmp = longSize;
-                longSize = shortSize;
-                shortSize = tmp;
-            }
-            longSize = (int)(longSize/dm.density);
-            shortSize = (int)(shortSize/dm.density);
+        // Compute the screen layout size class.
+        int screenLayout;
+        int longSize = dw;
+        int shortSize = dh;
+        if (longSize < shortSize) {
+            int tmp = longSize;
+            longSize = shortSize;
+            shortSize = tmp;
+        }
+        longSize = (int)(longSize/dm.density);
+        shortSize = (int)(shortSize/dm.density);
 
-            // These semi-magic numbers define our compatibility modes for
-            // applications with different screens.  These are guarantees to
-            // app developers about the space they can expect for a particular
-            // configuration.  DO NOT CHANGE!
-            if (longSize < 470) {
-                // This is shorter than an HVGA normal density screen (which
-                // is 480 pixels on its long side).
-                mScreenLayout = Configuration.SCREENLAYOUT_SIZE_SMALL
-                        | Configuration.SCREENLAYOUT_LONG_NO;
+        // These semi-magic numbers define our compatibility modes for
+        // applications with different screens.  These are guarantees to
+        // app developers about the space they can expect for a particular
+        // configuration.  DO NOT CHANGE!
+        if (longSize < 470) {
+            // This is shorter than an HVGA normal density screen (which
+            // is 480 pixels on its long side).
+            screenLayout = Configuration.SCREENLAYOUT_SIZE_SMALL
+                    | Configuration.SCREENLAYOUT_LONG_NO;
+        } else {
+            // What size is this screen screen?
+            if (longSize >= 960 && shortSize >= 720) {
+                // 1.5xVGA or larger screens at medium density are the point
+                // at which we consider it to be an extra large screen.
+                screenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE;
+            } else if (longSize >= 640 && shortSize >= 480) {
+                // VGA or larger screens at medium density are the point
+                // at which we consider it to be a large screen.
+                screenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE;
             } else {
-                // What size is this screen screen?
-                if (longSize >= 960 && shortSize >= 720) {
-                    // 1.5xVGA or larger screens at medium density are the point
-                    // at which we consider it to be an extra large screen.
-                    mScreenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE;
-                } else if (longSize >= 640 && shortSize >= 480) {
-                    // VGA or larger screens at medium density are the point
-                    // at which we consider it to be a large screen.
-                    mScreenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE;
-                } else {
-                    mScreenLayout = Configuration.SCREENLAYOUT_SIZE_NORMAL;
-                }
-                
-                // If this screen is wider than normal HVGA, or taller
-                // than FWVGA, then for old apps we want to run in size
-                // compatibility mode.
-                if (shortSize > 321 || longSize > 570) {
-                    mScreenLayout |= Configuration.SCREENLAYOUT_COMPAT_NEEDED;
-                }
+                screenLayout = Configuration.SCREENLAYOUT_SIZE_NORMAL;
+            }
 
-                // Is this a long screen?
-                if (((longSize*3)/5) >= (shortSize-1)) {
-                    // Anything wider than WVGA (5:3) is considering to be long.
-                    mScreenLayout |= Configuration.SCREENLAYOUT_LONG_YES;
-                } else {
-                    mScreenLayout |= Configuration.SCREENLAYOUT_LONG_NO;
-                }
+            // If this screen is wider than normal HVGA, or taller
+            // than FWVGA, then for old apps we want to run in size
+            // compatibility mode.
+            if (shortSize > 321 || longSize > 570) {
+                screenLayout |= Configuration.SCREENLAYOUT_COMPAT_NEEDED;
+            }
+
+            // Is this a long screen?
+            if (((longSize*3)/5) >= (shortSize-1)) {
+                // Anything wider than WVGA (5:3) is considering to be long.
+                screenLayout |= Configuration.SCREENLAYOUT_LONG_YES;
+            } else {
+                screenLayout |= Configuration.SCREENLAYOUT_LONG_NO;
             }
         }
-        config.screenLayout = mScreenLayout;
+        config.screenLayout = screenLayout;
 
         // Determine whether a hard keyboard is available and enabled.
         boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
@@ -6722,8 +6795,8 @@
         }
 
         final long currentTime = SystemClock.uptimeMillis();
-        final int dw = mCurDisplayWidth = mDisplay.getRealWidth();
-        final int dh = mCurDisplayHeight = mDisplay.getRealHeight();
+        final int dw = mCurDisplayWidth;
+        final int dh = mCurDisplayHeight;
 
         int i;
 
@@ -8709,8 +8782,9 @@
                     pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
                     pw.print(" mWaitingForConfig="); pw.println(mWaitingForConfig);
             pw.print("  mRotation="); pw.print(mRotation);
-                    pw.print(", mForcedAppOrientation="); pw.print(mForcedAppOrientation);
-                    pw.print(", mRequestedRotation="); pw.println(mRequestedRotation);
+                    pw.print(" mForcedAppOrientation="); pw.print(mForcedAppOrientation);
+                    pw.print(" mRequestedRotation="); pw.print(mRequestedRotation);
+                    pw.print(" mAltOrientation="); pw.println(mAltOrientation);
             pw.print("  mDeferredRotation="); pw.print(mDeferredRotation);
                     pw.print(", mDeferredRotationAnimFlags="); pw.println(mDeferredRotationAnimFlags);
             pw.print("  mAnimationPending="); pw.print(mAnimationPending);
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
index 2f9b026..76031a8 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
@@ -153,7 +153,8 @@
         float[] advances = new float[length];
         float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, dir, advances, 0);
         setPaintDir(paint, dir);
-        float textWidthICU = paint.getTextRunAdvancesICU(text, 0, length, 0, length, dir, advances, 0);
+        float textWidthICU = paint.getTextRunAdvances(text, 0, length, 0, length, dir, advances, 0,
+                1 /* use ICU */);
 
         logAdvances(text, textWidthHB, textWidthICU, advances);
         drawMetricsAroundText(canvas, x, y, textWidthHB, textWidthICU, textSize, Color.RED, Color.GREEN);
diff --git a/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml b/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml
index 69a33bc..174cc65 100644
--- a/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml
+++ b/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml
@@ -2,10 +2,10 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.rs.image">
-
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />    
     <uses-sdk android:minSdkVersion="11" />
-    <application android:label="Image Processing">
+    <application android:label="Image Processing"
+                 android:hardwareAccelerated="true">
         <activity android:name="ImageProcessingActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable-hdpi/data.jpg b/tests/RenderScriptTests/ImageProcessing/res/drawable-hdpi/data.jpg
deleted file mode 100644
index 81a87b1..0000000
--- a/tests/RenderScriptTests/ImageProcessing/res/drawable-hdpi/data.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable/city.png b/tests/RenderScriptTests/ImageProcessing/res/drawable/city.png
new file mode 100644
index 0000000..856eeff
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/res/drawable/city.png
Binary files differ
diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable/data.jpg b/tests/RenderScriptTests/ImageProcessing/res/drawable/data.jpg
deleted file mode 100644
index 81a87b1..0000000
--- a/tests/RenderScriptTests/ImageProcessing/res/drawable/data.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml b/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml
index b271b43..08a010d 100644
--- a/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml
+++ b/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml
@@ -17,31 +17,12 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:orientation="vertical"
             android:layout_width="fill_parent"
-            android:layout_height="fill_parent">
+            android:layout_height="fill_parent"
+            android:id="@+id/toplevel">
     <SurfaceView
         android:id="@+id/surface"
         android:layout_width="1dip"
         android:layout_height="1dip" />
-    <ImageView
-        android:id="@+id/display"
-        android:layout_width="320dip"
-        android:layout_height="266dip" />
-    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:orientation="horizontal"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content">
-            <Button
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/benchmark"
-                android:onClick="benchmark"/>
-            <TextView
-                android:id="@+id/benchmarkText"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:textSize="8pt"
-                android:text="@string/saturation"/>
-    </LinearLayout>
     <ScrollView
         android:layout_width="fill_parent"
         android:layout_height="fill_parent">
@@ -49,6 +30,26 @@
                 android:orientation="vertical"
                 android:layout_width="fill_parent"
                 android:layout_height="fill_parent">
+            <ImageView
+                android:id="@+id/display"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:orientation="horizontal"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content">
+                    <Button
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/benchmark"
+                        android:onClick="benchmark"/>
+                    <TextView
+                        android:id="@+id/benchmarkText"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:textSize="8pt"
+                        android:text="@string/saturation"/>
+            </LinearLayout>
             <TextView
                 android:id="@+id/inSaturationText"
                 android:layout_width="match_parent"
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 4f2f52ab..7462701 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -40,7 +40,6 @@
                                        SeekBar.OnSeekBarChangeListener {
     private Bitmap mBitmapIn;
     private Bitmap mBitmapOut;
-    private Bitmap mBitmapScratch;
     private ScriptC_threshold mScript;
     private ScriptC_vertical_blur mScriptVBlur;
     private ScriptC_horizontal_blur mScriptHBlur;
@@ -78,9 +77,20 @@
     private SurfaceView mSurfaceView;
     private ImageView mDisplayView;
 
+    private boolean mIsProcessing;
+
     class FilterCallback extends RenderScript.RSMessageHandler {
         private Runnable mAction = new Runnable() {
             public void run() {
+
+                synchronized (mDisplayView) {
+                    mIsProcessing = false;
+                }
+
+                // This is a hack to work around an invalidation bug
+                mBitmapOut = Bitmap.createBitmap(mBitmapOut);
+                mOutPixelsAllocation.copyTo(mBitmapOut);
+                mDisplayView.setImageBitmap(mBitmapOut);
                 mDisplayView.invalidate();
             }
         };
@@ -99,156 +109,6 @@
     // Store our coefficients here
     float gaussian[];
 
-    private long javaFilter() {
-        final int width = mBitmapIn.getWidth();
-        final int height = mBitmapIn.getHeight();
-        final int count = width * height;
-
-        if (in == null) {
-            in = new int[count];
-            interm = new int[count];
-            out = new int[count];
-            gaussian = new float[MAX_RADIUS * 2 + 1];
-            mBitmapIn.getPixels(in, 0, width, 0, 0, width, height);
-        }
-
-        long t = java.lang.System.currentTimeMillis();
-
-        int w, h, r;
-
-        float fRadius = (float)mRadius;
-        int radius = (int)mRadius;
-
-        // Compute gaussian weights for the blur
-        // e is the euler's number
-        float e = 2.718281828459045f;
-        float pi = 3.1415926535897932f;
-        // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
-        // x is of the form [-radius .. 0 .. radius]
-        // and sigma varies with radius.
-        // Based on some experimental radius values and sigma's
-        // we approximately fit sigma = f(radius) as
-        // sigma = radius * 0.4  + 0.6
-        // The larger the radius gets, the more our gaussian blur
-        // will resemble a box blur since with large sigma
-        // the gaussian curve begins to lose its shape
-        float sigma = 0.4f * fRadius + 0.6f;
-        // Now compute the coefficints
-        // We will store some redundant values to save some math during
-        // the blur calculations
-        // precompute some values
-        float coeff1 = 1.0f / (float)(Math.sqrt( 2.0f * pi ) * sigma);
-        float coeff2 = - 1.0f / (2.0f * sigma * sigma);
-        float normalizeFactor = 0.0f;
-        float floatR = 0.0f;
-        for (r = -radius; r <= radius; r ++) {
-            floatR = (float)r;
-            gaussian[r + radius] = coeff1 * (float)Math.pow(e, floatR * floatR * coeff2);
-            normalizeFactor += gaussian[r + radius];
-        }
-
-        //Now we need to normalize the weights because all our coefficients need to add up to one
-        normalizeFactor = 1.0f / normalizeFactor;
-        for (r = -radius; r <= radius; r ++) {
-            floatR = (float)r;
-            gaussian[r + radius] *= normalizeFactor;
-        }
-
-        float blurredPixelR = 0.0f;
-        float blurredPixelG = 0.0f;
-        float blurredPixelB = 0.0f;
-        float blurredPixelA = 0.0f;
-
-        for (h = 0; h < height; h ++) {
-            for (w = 0; w < width; w ++) {
-
-                blurredPixelR = 0.0f;
-                blurredPixelG = 0.0f;
-                blurredPixelB = 0.0f;
-                blurredPixelA = 0.0f;
-
-                for (r = -radius; r <= radius; r ++) {
-                    // Stepping left and right away from the pixel
-                    int validW = w + r;
-                    // Clamp to zero and width max() isn't exposed for ints yet
-                    if (validW < 0) {
-                        validW = 0;
-                    }
-                    if (validW > width - 1) {
-                        validW = width - 1;
-                    }
-
-                    int input = in[h*width + validW];
-
-                    int R = ((input >> 24) & 0xff);
-                    int G = ((input >> 16) & 0xff);
-                    int B = ((input >> 8) & 0xff);
-                    int A = (input & 0xff);
-
-                    float weight = gaussian[r + radius];
-
-                    blurredPixelR += (float)(R)*weight;
-                    blurredPixelG += (float)(G)*weight;
-                    blurredPixelB += (float)(B)*weight;
-                    blurredPixelA += (float)(A)*weight;
-                }
-
-                int R = (int)blurredPixelR;
-                int G = (int)blurredPixelG;
-                int B = (int)blurredPixelB;
-                int A = (int)blurredPixelA;
-
-                interm[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A);
-            }
-        }
-
-        for (h = 0; h < height; h ++) {
-            for (w = 0; w < width; w ++) {
-
-                blurredPixelR = 0.0f;
-                blurredPixelG = 0.0f;
-                blurredPixelB = 0.0f;
-                blurredPixelA = 0.0f;
-                for (r = -radius; r <= radius; r ++) {
-                    int validH = h + r;
-                    // Clamp to zero and width
-                    if (validH < 0) {
-                        validH = 0;
-                    }
-                    if (validH > height - 1) {
-                        validH = height - 1;
-                    }
-
-                    int input = interm[validH*width + w];
-
-                    int R = ((input >> 24) & 0xff);
-                    int G = ((input >> 16) & 0xff);
-                    int B = ((input >> 8) & 0xff);
-                    int A = (input & 0xff);
-
-                    float weight = gaussian[r + radius];
-
-                    blurredPixelR += (float)(R)*weight;
-                    blurredPixelG += (float)(G)*weight;
-                    blurredPixelB += (float)(B)*weight;
-                    blurredPixelA += (float)(A)*weight;
-                }
-
-                int R = (int)blurredPixelR;
-                int G = (int)blurredPixelG;
-                int B = (int)blurredPixelB;
-                int A = (int)blurredPixelA;
-
-                out[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A);
-            }
-        }
-
-        t = java.lang.System.currentTimeMillis() - t;
-        android.util.Log.v("Img", "Java frame time ms " + t);
-        mBitmapOut.setPixels(out, 0, width, 0, 0, width, height);
-        return t;
-    }
-
     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
         if (fromUser) {
 
@@ -280,17 +140,14 @@
                 mScriptVBlur.invoke_setSaturation(mSaturation);
             }
 
-            long t = java.lang.System.currentTimeMillis();
-            if (true) {
-                mScript.invoke_filter();
-                mOutPixelsAllocation.copyTo(mBitmapOut);
-            } else {
-                javaFilter();
-                mDisplayView.invalidate();
+            synchronized (mDisplayView) {
+                if (mIsProcessing) {
+                    return;
+                }
+                mIsProcessing = true;
             }
 
-            t = java.lang.System.currentTimeMillis() - t;
-            android.util.Log.v("Img", "Renderscript frame time core ms " + t);
+            mScript.invoke_filter();
         }
     }
 
@@ -305,9 +162,8 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
 
-        mBitmapIn = loadBitmap(R.drawable.data);
-        mBitmapOut = loadBitmap(R.drawable.data);
-        mBitmapScratch = loadBitmap(R.drawable.data);
+        mBitmapIn = loadBitmap(R.drawable.city);
+        mBitmapOut = loadBitmap(R.drawable.city);
 
         mSurfaceView = (SurfaceView) findViewById(R.id.surface);
         mSurfaceView.getHolder().addCallback(this);
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
index 652ffd7..45eea5e 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
@@ -19,7 +19,7 @@
     } else {
         for (int r = -fs->radius; r <= fs->radius; r ++) {
             // Stepping left and right away from the pixel
-            int validW = rsClamp(x + r, (uint)0, (uint)(fs->width - 1));
+            int validW = rsClamp((int)x + r, (int)0, (int)(fs->width - 1));
             blurredPixel += input[validW].xyz * gPtr[0];
             gPtr++;
         }
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
index bd4ae4e..6b0cde0 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
@@ -74,7 +74,7 @@
         }
     } else {
         for (int r = -fs->radius; r <= fs->radius; r ++) {
-            int validH = rsClamp(y + r, (uint)0, (uint)(fs->height - 1));
+            int validH = rsClamp((int)y + r, (int)0, (int)(fs->height - 1));
             const float4 *i = input + validH * fs->width;
             blurredPixel += i->xyz * gPtr[0];
             gPtr++;