Merge "Expose Credentials.UNLOCK_ACTION for callers that want to use startActivityForResult"
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/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/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 8110d8e..4d3aa68 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -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,
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/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7f18121..2ed39e4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -550,6 +550,14 @@
         android:description="@string/permdesc_sdcardWrite"
         android:protectionLevel="dangerous" />
 
+    <!-- Allows an application to write to internal media storage
+         @hide  -->
+    <permission android:name="android.permission.WRITE_MEDIA_STORAGE"
+        android:permissionGroup="android.permission-group.STORAGE"
+        android:label="@string/permlab_mediaStorageWrite"
+        android:description="@string/permdesc_mediaStorageWrite"
+        android:protectionLevel="signatureOrSystem" />
+
     <!-- ============================================ -->
     <!-- Permissions for low-level system interaction -->
     <!-- ============================================ -->
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index df80546..1870a4a 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -58,6 +58,10 @@
         <group gid="sdcard_rw" />
     </permission>
 
+    <permission name="android.permission.WRITE_MEDIA_STORAGE" >
+        <group gid="media_rw" />
+    </permission>
+
     <permission name="android.permission.ACCESS_MTP" >
         <group gid="mtp" />
     </permission>
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/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/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 06708da..585cd30 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -29,6 +29,7 @@
 
 #include "MtpDatabase.h"
 #include "MtpDataPacket.h"
+#include "MtpObjectInfo.h"
 #include "MtpProperty.h"
 #include "MtpStringBuffer.h"
 #include "MtpUtils.h"
@@ -138,7 +139,7 @@
                                             MtpDataPacket& packet);
 
     virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
-                                            MtpDataPacket& packet);
+                                            MtpObjectInfo& info);
 
     virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
                                             MtpString& outFilePath,
@@ -744,7 +745,7 @@
 }
 
 MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
-                                            MtpDataPacket& packet) {
+                                            MtpObjectInfo& info) {
     char    date[20];
 
     JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -754,46 +755,27 @@
         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
 
     jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
-    MtpStorageID storageID = intValues[0];
-    MtpObjectFormat format = intValues[1];
-    MtpObjectHandle parent = intValues[2];
+    info.mStorageID = intValues[0];
+    info.mFormat = intValues[1];
+    info.mParent = intValues[2];
     env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
 
     jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
     uint64_t size = longValues[0];
-    uint64_t modified = longValues[1];
+    info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
+    info.mDateModified = longValues[1];
     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
 
-//    int associationType = (format == MTP_FORMAT_ASSOCIATION ?
+//    info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ?
 //                            MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
 //                            MTP_ASSOCIATION_TYPE_UNDEFINED);
-    int associationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
-
-    packet.putUInt32(storageID);
-    packet.putUInt16(format);
-    packet.putUInt16(0);   // protection status
-    packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size));
-    packet.putUInt16(0);   // thumb format
-    packet.putUInt32(0);   // thumb compressed size
-    packet.putUInt32(0);   // thumb pix width
-    packet.putUInt32(0);   // thumb pix height
-    packet.putUInt32(0);   // image pix width
-    packet.putUInt32(0);   // image pix height
-    packet.putUInt32(0);   // image bit depth
-    packet.putUInt32(parent);
-    packet.putUInt16(associationType);
-    packet.putUInt32(0);   // association desc
-    packet.putUInt32(0);   // sequence number
+    info.mAssociationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
 
     jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
-    packet.putString(str);   // file name
+    MtpString temp(str);
+    info.mName = strdup((const char *)temp);
     env->ReleaseCharArrayElements(mStringBuffer, str, 0);
 
-    packet.putEmptyString();
-    formatDateTime(modified, date, sizeof(date));
-    packet.putString(date);   // date modified
-    packet.putEmptyString();   // keywords
-
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return MTP_RESPONSE_OK;
 }
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index 4d9a1ae..d7bde00 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -23,6 +23,7 @@
 
 class MtpDataPacket;
 class MtpProperty;
+class MtpObjectInfo;
 
 class MtpDatabase {
 public:
@@ -81,7 +82,7 @@
                                             MtpDataPacket& packet) = 0;
 
     virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
-                                            MtpDataPacket& packet) = 0;
+                                            MtpObjectInfo& info) = 0;
 
     virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
                                             MtpString& outFilePath,
diff --git a/media/mtp/MtpDebug.cpp b/media/mtp/MtpDebug.cpp
index 1668ecf..9f3037d 100644
--- a/media/mtp/MtpDebug.cpp
+++ b/media/mtp/MtpDebug.cpp
@@ -63,6 +63,12 @@
     { "MTP_OPERATION_GET_OBJECT_REFERENCES",        0x9810 },
     { "MTP_OPERATION_SET_OBJECT_REFERENCES",        0x9811 },
     { "MTP_OPERATION_SKIP",                         0x9820 },
+    // android extensions
+    { "MTP_OPERATION_GET_PARTIAL_OBJECT_64",        0x95C1 },
+    { "MTP_OPERATION_SEND_PARTIAL_OBJECT",          0x95C2 },
+    { "MTP_OPERATION_TRUNCATE_OBJECT",              0x95C3 },
+    { "MTP_OPERATION_BEGIN_EDIT_OBJECT",            0x95C4 },
+    { "MTP_OPERATION_END_EDIT_OBJECT",              0x95C5 },
     { 0,                                            0      },
 };
 
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 37e02a3..ff4009c 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -30,6 +30,7 @@
 
 #include "MtpDebug.h"
 #include "MtpDatabase.h"
+#include "MtpObjectInfo.h"
 #include "MtpProperty.h"
 #include "MtpServer.h"
 #include "MtpStorage.h"
@@ -79,6 +80,12 @@
     MTP_OPERATION_GET_OBJECT_REFERENCES,
     MTP_OPERATION_SET_OBJECT_REFERENCES,
 //    MTP_OPERATION_SKIP,
+    // Android extension for direct file IO
+    MTP_OPERATION_GET_PARTIAL_OBJECT_64,
+    MTP_OPERATION_SEND_PARTIAL_OBJECT,
+    MTP_OPERATION_TRUNCATE_OBJECT,
+    MTP_OPERATION_BEGIN_EDIT_OBJECT,
+    MTP_OPERATION_END_EDIT_OBJECT,
 };
 
 static const MtpEventCode kSupportedEventCodes[] = {
@@ -218,6 +225,15 @@
         }
     }
 
+    // commit any open edits
+    int count = mObjectEditList.size();
+    for (int i = 0; i < count; i++) {
+        ObjectEdit* edit = mObjectEditList[i];
+        commitEdit(edit);
+        delete edit;
+    }
+    mObjectEditList.clear();
+
     if (mSessionOpen)
         mDatabase->sessionEnded();
 }
@@ -252,6 +268,44 @@
     }
 }
 
+void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
+        uint64_t size, MtpObjectFormat format, int fd) {
+    ObjectEdit*  edit = new ObjectEdit;
+    edit->handle = handle;
+    edit->path = path;
+    edit->size = size;
+    edit->format = format;
+    edit->fd = fd;
+    mObjectEditList.add(edit);
+}
+
+MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
+    int count = mObjectEditList.size();
+    for (int i = 0; i < count; i++) {
+        ObjectEdit* edit = mObjectEditList[i];
+        if (edit->handle == handle) return edit;
+    }
+    return NULL;
+}
+
+void MtpServer::removeEditObject(MtpObjectHandle handle) {
+    int count = mObjectEditList.size();
+    for (int i = 0; i < count; i++) {
+        ObjectEdit* edit = mObjectEditList[i];
+        if (edit->handle == handle) {
+            delete edit;
+            mObjectEditList.removeAt(i);
+            return;
+        }
+    }
+    LOGE("ObjectEdit not found in removeEditObject");
+}
+
+void MtpServer::commitEdit(ObjectEdit* edit) {
+    mDatabase->endSendObject((const char *)edit->path, edit->handle, edit->format, true);
+}
+
+
 bool MtpServer::handleRequest() {
     Mutex::Autolock autoLock(mMutex);
 
@@ -322,7 +376,8 @@
             response = doGetObject();
             break;
         case MTP_OPERATION_GET_PARTIAL_OBJECT:
-            response = doGetPartialObject();
+        case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
+            response = doGetPartialObject(operation);
             break;
         case MTP_OPERATION_SEND_OBJECT_INFO:
             response = doSendObjectInfo();
@@ -339,6 +394,18 @@
         case MTP_OPERATION_GET_DEVICE_PROP_DESC:
             response = doGetDevicePropDesc();
             break;
+        case MTP_OPERATION_SEND_PARTIAL_OBJECT:
+            response = doSendPartialObject();
+            break;
+        case MTP_OPERATION_TRUNCATE_OBJECT:
+            response = doTruncateObject();
+            break;
+        case MTP_OPERATION_BEGIN_EDIT_OBJECT:
+            response = doBeginEditObject();
+            break;
+        case MTP_OPERATION_END_EDIT_OBJECT:
+            response = doEndEditObject();
+            break;
         default:
             LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
             response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
@@ -363,7 +430,7 @@
     mData.putUInt16(MTP_STANDARD_VERSION);
     mData.putUInt32(6); // MTP Vendor Extension ID
     mData.putUInt16(MTP_STANDARD_VERSION);
-    string.set("microsoft.com: 1.0;");
+    string.set("microsoft.com: 1.0; android.com: 1.0;");
     mData.putString(string); // MTP Extensions
     mData.putUInt16(0); //Functional Mode
     mData.putAUInt16(kSupportedOperationCodes,
@@ -601,7 +668,40 @@
     if (!hasStorage())
         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     MtpObjectHandle handle = mRequest.getParameter(1);
-    return mDatabase->getObjectInfo(handle, mData);
+    MtpObjectInfo info(handle);
+    MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
+    if (result == MTP_RESPONSE_OK) {
+        char    date[20];
+
+        mData.putUInt32(info.mStorageID);
+        mData.putUInt16(info.mFormat);
+        mData.putUInt16(info.mProtectionStatus);
+
+        // if object is being edited the database size may be out of date
+        uint32_t size = info.mCompressedSize;
+        ObjectEdit* edit = getEditObject(handle);
+        if (edit)
+            size = (edit->size > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->size);
+        mData.putUInt32(size);
+
+        mData.putUInt16(info.mThumbFormat);
+        mData.putUInt32(info.mThumbCompressedSize);
+        mData.putUInt32(info.mThumbPixWidth);
+        mData.putUInt32(info.mThumbPixHeight);
+        mData.putUInt32(info.mImagePixWidth);
+        mData.putUInt32(info.mImagePixHeight);
+        mData.putUInt32(info.mImagePixDepth);
+        mData.putUInt32(info.mParent);
+        mData.putUInt16(info.mAssociationType);
+        mData.putUInt32(info.mAssociationDesc);
+        mData.putUInt32(info.mSequenceNumber);
+        mData.putString(info.mName);
+        mData.putEmptyString();    // date created
+        formatDateTime(info.mDateModified, date, sizeof(date));
+        mData.putString(date);   // date modified
+        mData.putEmptyString();   // keywords
+    }
+    return result;
 }
 
 MtpResponseCode MtpServer::doGetObject() {
@@ -641,12 +741,22 @@
     return MTP_RESPONSE_OK;
 }
 
-MtpResponseCode MtpServer::doGetPartialObject() {
+MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
     if (!hasStorage())
         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     MtpObjectHandle handle = mRequest.getParameter(1);
-    uint32_t offset = mRequest.getParameter(2);
-    uint32_t length = mRequest.getParameter(3);
+    uint64_t offset;
+    uint32_t length;
+    offset = mRequest.getParameter(2);
+    if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
+        // android extension with 64 bit offset
+        uint64_t offset2 = mRequest.getParameter(3);
+        offset = offset | (offset2 << 32);
+        length = mRequest.getParameter(4);
+    } else {
+        // standard GetPartialObject
+        length = mRequest.getParameter(3);
+    }
     MtpString pathBuf;
     int64_t fileLength;
     MtpObjectFormat format;
@@ -933,4 +1043,113 @@
     return MTP_RESPONSE_OK;
 }
 
+MtpResponseCode MtpServer::doSendPartialObject() {
+    if (!hasStorage())
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    uint64_t offset = mRequest.getParameter(2);
+    uint64_t offset2 = mRequest.getParameter(3);
+    offset = offset | (offset2 << 32);
+    uint32_t length = mRequest.getParameter(4);
+
+    ObjectEdit* edit = getEditObject(handle);
+    if (!edit) {
+        LOGE("object not open for edit in doSendPartialObject");
+        return MTP_RESPONSE_GENERAL_ERROR;
+    }
+
+    // can't start writing past the end of the file
+    if (offset > edit->size) {
+        LOGD("writing past end of object, offset: %lld, edit->size: %lld", offset, edit->size);
+        return MTP_RESPONSE_GENERAL_ERROR;
+    }
+
+    // read the header
+    int ret = mData.readDataHeader(mFD);
+    // FIXME - check for errors here.
+
+    // reset so we don't attempt to send this back
+    mData.reset();
+
+    const char* filePath = (const char *)edit->path;
+    LOGV("receiving partial %s %lld %ld\n", filePath, offset, length);
+    mtp_file_range  mfr;
+    mfr.fd = edit->fd;
+    mfr.offset = offset;
+    mfr.length = length;
+
+    // transfer the file
+    ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
+    LOGV("MTP_RECEIVE_FILE returned %d", ret);
+    if (ret < 0) {
+        mResponse.setParameter(1, 0);
+        if (errno == ECANCELED)
+            return MTP_RESPONSE_TRANSACTION_CANCELLED;
+        else
+            return MTP_RESPONSE_GENERAL_ERROR;
+    }
+    mResponse.setParameter(1, length);
+    uint64_t end = offset + length;
+    if (end > edit->size) {
+        edit->size = end;
+    }
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doTruncateObject() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    ObjectEdit* edit = getEditObject(handle);
+    if (!edit) {
+        LOGE("object not open for edit in doTruncateObject");
+        return MTP_RESPONSE_GENERAL_ERROR;
+    }
+
+    uint64_t offset = mRequest.getParameter(2);
+    uint64_t offset2 = mRequest.getParameter(3);
+    offset |= (offset2 << 32);
+    if (ftruncate(edit->fd, offset) != 0) {
+        return MTP_RESPONSE_GENERAL_ERROR;
+    } else {
+        edit->size = offset;
+        return MTP_RESPONSE_OK;
+    }
+}
+
+MtpResponseCode MtpServer::doBeginEditObject() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    if (getEditObject(handle)) {
+        LOGE("object already open for edit in doBeginEditObject");
+        return MTP_RESPONSE_GENERAL_ERROR;
+    }
+
+    MtpString path;
+    int64_t fileLength;
+    MtpObjectFormat format;
+    int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
+    if (result != MTP_RESPONSE_OK)
+        return result;
+
+    int fd = open((const char *)path, O_RDWR | O_EXCL);
+    if (fd < 0) {
+        LOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
+        return MTP_RESPONSE_GENERAL_ERROR;
+    }
+
+    addEditObject(handle, path, fileLength, format, fd);
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doEndEditObject() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    ObjectEdit* edit = getEditObject(handle);
+    if (!edit) {
+        LOGE("object not open for edit in doEndEditObject");
+        return MTP_RESPONSE_GENERAL_ERROR;
+    }
+
+    commitEdit(edit);
+    removeEditObject(handle);
+    return MTP_RESPONSE_OK;
+}
+
 }  // namespace android
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index fa729a8..35a38e7 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -65,6 +65,17 @@
 
     Mutex               mMutex;
 
+    // represents an MTP object that is being edited using the android extensions
+    // for direct editing (BeginEditObject, SendPartialObject, TruncateObject and EndEditObject)
+    struct ObjectEdit {
+        MtpObjectHandle     handle;
+        MtpString           path;
+        uint64_t            size;
+        MtpObjectFormat     format;
+        int                 fd;
+    };
+    Vector<ObjectEdit*>  mObjectEditList;
+
 public:
                         MtpServer(int fd, MtpDatabase* database,
                                     int fileGroup, int filePerm, int directoryPerm);
@@ -86,6 +97,12 @@
     void                sendStoreRemoved(MtpStorageID id);
     void                sendEvent(MtpEventCode code, uint32_t param1);
 
+    void                addEditObject(MtpObjectHandle handle, MtpString& path,
+                                uint64_t size, MtpObjectFormat format, int fd);
+    ObjectEdit*         getEditObject(MtpObjectHandle handle);
+    void                removeEditObject(MtpObjectHandle handle);
+    void                commitEdit(ObjectEdit* edit);
+
     bool                handleRequest();
 
     MtpResponseCode     doGetDeviceInfo();
@@ -106,12 +123,16 @@
     MtpResponseCode     doGetObjectPropList();
     MtpResponseCode     doGetObjectInfo();
     MtpResponseCode     doGetObject();
-    MtpResponseCode     doGetPartialObject();
+    MtpResponseCode     doGetPartialObject(MtpOperationCode operation);
     MtpResponseCode     doSendObjectInfo();
     MtpResponseCode     doSendObject();
     MtpResponseCode     doDeleteObject();
     MtpResponseCode     doGetObjectPropDesc();
     MtpResponseCode     doGetDevicePropDesc();
+    MtpResponseCode     doSendPartialObject();
+    MtpResponseCode     doTruncateObject();
+    MtpResponseCode     doBeginEditObject();
+    MtpResponseCode     doEndEditObject();
 };
 
 }; // namespace android
diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h
index 8bc2e22..d270df5 100644
--- a/media/mtp/mtp.h
+++ b/media/mtp/mtp.h
@@ -391,6 +391,19 @@
 #define MTP_OPERATION_SET_OBJECT_REFERENCES                 0x9811
 #define MTP_OPERATION_SKIP                                  0x9820
 
+// Android extensions for direct file IO
+
+// Same as GetPartialObject, but with 64 bit offset
+#define MTP_OPERATION_GET_PARTIAL_OBJECT_64                 0x95C1
+// Same as GetPartialObject64, but copying host to device
+#define MTP_OPERATION_SEND_PARTIAL_OBJECT                   0x95C2
+// Truncates file to 64 bit length
+#define MTP_OPERATION_TRUNCATE_OBJECT                       0x95C3
+// Must be called before using SendPartialObject and TruncateObject
+#define MTP_OPERATION_BEGIN_EDIT_OBJECT                     0x95C4
+// Called to commit changes made by SendPartialObject and TruncateObject
+#define MTP_OPERATION_END_EDIT_OBJECT                       0x95C5
+
 // MTP Response Codes
 #define MTP_RESPONSE_UNDEFINED                                  0x2000
 #define MTP_RESPONSE_OK                                         0x2001
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/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/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);