Merge "Close streams for forward-locked apps"
diff --git a/Android.mk b/Android.mk
index 4e3929c..ab06058 100644
--- a/Android.mk
+++ b/Android.mk
@@ -60,7 +60,7 @@
 ## READ ME: ########################################################
 LOCAL_SRC_FILES += \
 	core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
-	core/java/android/accessibilityservice/IEventListener.aidl \
+	core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
 	core/java/android/accounts/IAccountManager.aidl \
 	core/java/android/accounts/IAccountManagerResponse.aidl \
 	core/java/android/accounts/IAccountAuthenticator.aidl \
diff --git a/api/16.txt b/api/16.txt
index de99eee..0e3a140 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -15190,11 +15190,11 @@
     method public abstract void released();
   }
 
-  public class Vibrator {
-    method public void cancel();
-    method public boolean hasVibrator();
-    method public void vibrate(long);
-    method public void vibrate(long[], int);
+  public abstract class Vibrator {
+    method public abstract void cancel();
+    method public abstract boolean hasVibrator();
+    method public abstract void vibrate(long);
+    method public abstract void vibrate(long[], int);
   }
 
   public class WorkSource implements android.os.Parcelable {
diff --git a/api/current.txt b/api/current.txt
index eacce52..2f8dcb1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -534,6 +534,7 @@
     field public static final int imeSubtypeLocale = 16843500; // 0x10102ec
     field public static final int imeSubtypeMode = 16843501; // 0x10102ed
     field public static final int immersive = 16843456; // 0x10102c0
+    field public static final int importantForAccessibility = 16843699; // 0x10103b3
     field public static final int inAnimation = 16843127; // 0x1010177
     field public static final int includeFontPadding = 16843103; // 0x101015f
     field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -1993,11 +1994,23 @@
 
   public abstract class AccessibilityService extends android.app.Service {
     ctor public AccessibilityService();
+    method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
     method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public final android.os.IBinder onBind(android.content.Intent);
+    method protected void onGesture(int);
     method public abstract void onInterrupt();
     method protected void onServiceConnected();
     method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
+    field public static final int GESTURE_CLOCKWISE_CIRCLE = 9; // 0x9
+    field public static final int GESTURE_COUNTER_CLOCKWISE_CIRCLE = 10; // 0xa
+    field public static final int GESTURE_SWIPE_DOWN = 2; // 0x2
+    field public static final int GESTURE_SWIPE_DOWN_AND_UP = 8; // 0x8
+    field public static final int GESTURE_SWIPE_LEFT = 3; // 0x3
+    field public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5; // 0x5
+    field public static final int GESTURE_SWIPE_RIGHT = 4; // 0x4
+    field public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6; // 0x6
+    field public static final int GESTURE_SWIPE_UP = 1; // 0x1
+    field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7
     field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
   }
@@ -2022,6 +2035,7 @@
     field public static final int FEEDBACK_HAPTIC = 2; // 0x2
     field public static final int FEEDBACK_SPOKEN = 1; // 0x1
     field public static final int FEEDBACK_VISUAL = 8; // 0x8
+    field public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
     field public int eventTypes;
     field public int feedbackType;
     field public int flags;
@@ -4833,6 +4847,7 @@
     method public android.content.ClipDescription getDescription();
     method public android.content.ClipData.Item getItemAt(int);
     method public int getItemCount();
+    method public static android.content.ClipData newHtmlText(java.lang.CharSequence, java.lang.CharSequence, java.lang.String);
     method public static android.content.ClipData newIntent(java.lang.CharSequence, android.content.Intent);
     method public static android.content.ClipData newPlainText(java.lang.CharSequence, java.lang.CharSequence);
     method public static android.content.ClipData newRawUri(java.lang.CharSequence, android.net.Uri);
@@ -4843,10 +4858,15 @@
 
   public static class ClipData.Item {
     ctor public ClipData.Item(java.lang.CharSequence);
+    ctor public ClipData.Item(java.lang.CharSequence, java.lang.String);
     ctor public ClipData.Item(android.content.Intent);
     ctor public ClipData.Item(android.net.Uri);
     ctor public ClipData.Item(java.lang.CharSequence, android.content.Intent, android.net.Uri);
+    ctor public ClipData.Item(java.lang.CharSequence, java.lang.String, android.content.Intent, android.net.Uri);
+    method public java.lang.String coerceToHtmlText(android.content.Context);
+    method public java.lang.CharSequence coerceToStyledText(android.content.Context);
     method public java.lang.CharSequence coerceToText(android.content.Context);
+    method public java.lang.String getHtmlText();
     method public android.content.Intent getIntent();
     method public java.lang.CharSequence getText();
     method public android.net.Uri getUri();
@@ -4864,6 +4884,7 @@
     method public boolean hasMimeType(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final java.lang.String MIMETYPE_TEXT_HTML = "text/html";
     field public static final java.lang.String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
     field public static final java.lang.String MIMETYPE_TEXT_PLAIN = "text/plain";
     field public static final java.lang.String MIMETYPE_TEXT_URILIST = "text/uri-list";
@@ -5701,6 +5722,7 @@
     field public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; // 0x0
     field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
     field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+    field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
     field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
     field public static final java.lang.String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_INTENT = "android.intent.extra.INTENT";
@@ -15506,11 +15528,11 @@
     ctor public TransactionTooLargeException();
   }
 
-  public class Vibrator {
-    method public void cancel();
-    method public boolean hasVibrator();
-    method public void vibrate(long);
-    method public void vibrate(long[], int);
+  public abstract class Vibrator {
+    method public abstract void cancel();
+    method public abstract boolean hasVibrator();
+    method public abstract void vibrate(long);
+    method public abstract void vibrate(long[], int);
   }
 
   public class WorkSource implements android.os.Parcelable {
@@ -20406,6 +20428,7 @@
   }
 
   public class Html {
+    method public static java.lang.String escapeHtml(java.lang.CharSequence);
     method public static android.text.Spanned fromHtml(java.lang.String);
     method public static android.text.Spanned fromHtml(java.lang.String, android.text.Html.ImageGetter, android.text.Html.TagHandler);
     method public static java.lang.String toHtml(android.text.Spanned);
@@ -22353,6 +22376,7 @@
     method public java.util.List<android.view.InputDevice.MotionRange> getMotionRanges();
     method public java.lang.String getName();
     method public int getSources();
+    method public android.os.Vibrator getVibrator();
     method public boolean isVirtual();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
@@ -23317,6 +23341,7 @@
     ctor public View(android.content.Context);
     ctor public View(android.content.Context, android.util.AttributeSet);
     ctor public View(android.content.Context, android.util.AttributeSet, int);
+    method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
     method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
@@ -23419,6 +23444,7 @@
     method public int getHorizontalFadingEdgeLength();
     method protected int getHorizontalScrollbarHeight();
     method public int getId();
+    method public int getImportantForAccessibility();
     method public boolean getKeepScreenOn();
     method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
     method public int getLayerType();
@@ -23452,6 +23478,7 @@
     method public int getPaddingStart();
     method public int getPaddingTop();
     method public final android.view.ViewParent getParent();
+    method public android.view.ViewParent getParentForAccessibility();
     method public float getPivotX();
     method public float getPivotY();
     method public int getResolvedLayoutDirection();
@@ -23607,6 +23634,7 @@
     method public void onWindowSystemUiVisibilityChanged(int);
     method protected void onWindowVisibilityChanged(int);
     method protected boolean overScrollBy(int, int, int, int, int, int, int, int, boolean);
+    method public boolean performAccessibilityAction(int);
     method public boolean performClick();
     method public boolean performHapticFeedback(int);
     method public boolean performHapticFeedback(int, int);
@@ -23678,6 +23706,7 @@
     method public void setHorizontalScrollBarEnabled(boolean);
     method public void setHovered(boolean);
     method public void setId(int);
+    method public void setImportantForAccessibility(int);
     method public void setKeepScreenOn(boolean);
     method public void setLayerType(int, android.graphics.Paint);
     method public void setLayoutDirection(int);
@@ -23752,6 +23781,14 @@
     method protected boolean verifyDrawable(android.graphics.drawable.Drawable);
     method public boolean willNotCacheDrawing();
     method public boolean willNotDraw();
+    field public static final int ACCESSIBILITY_FOCUS_BACKWARD = 4097; // 0x1001
+    field public static final int ACCESSIBILITY_FOCUS_DOWN = 4226; // 0x1082
+    field public static final int ACCESSIBILITY_FOCUS_FORWARD = 4098; // 0x1002
+    field public static final int ACCESSIBILITY_FOCUS_IN = 4100; // 0x1004
+    field public static final int ACCESSIBILITY_FOCUS_LEFT = 4113; // 0x1011
+    field public static final int ACCESSIBILITY_FOCUS_OUT = 4104; // 0x1008
+    field public static final int ACCESSIBILITY_FOCUS_RIGHT = 4162; // 0x1042
+    field public static final int ACCESSIBILITY_FOCUS_UP = 4129; // 0x1021
     field public static final android.util.Property ALPHA;
     field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
@@ -23773,6 +23810,7 @@
     field protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     field protected static final int[] FOCUSED_STATE_SET;
     field protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET;
+    field public static final int FOCUS_ACCESSIBILITY = 4096; // 0x1000
     field public static final int FOCUS_BACKWARD = 1; // 0x1
     field public static final int FOCUS_DOWN = 130; // 0x82
     field public static final int FOCUS_FORWARD = 2; // 0x2
@@ -23781,6 +23819,9 @@
     field public static final int FOCUS_UP = 33; // 0x21
     field public static final int GONE = 8; // 0x8
     field public static final int HAPTIC_FEEDBACK_ENABLED = 268435456; // 0x10000000
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
     field public static final int INVISIBLE = 4; // 0x4
     field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
     field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -24205,6 +24246,7 @@
     method public abstract void focusableViewAvailable(android.view.View);
     method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
     method public abstract android.view.ViewParent getParent();
+    method public abstract android.view.ViewParent getParentForAccessibility();
     method public abstract void invalidateChild(android.view.View, android.graphics.Rect);
     method public abstract android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
     method public abstract boolean isLayoutRequested();
@@ -24606,6 +24648,8 @@
     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_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
     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
@@ -24646,6 +24690,8 @@
     method public void addChild(android.view.View, int);
     method public int describeContents();
     method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String);
+    method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
+    method public android.view.accessibility.AccessibilityNodeInfo focusSearch(int);
     method public int getActions();
     method public void getBoundsInParent(android.graphics.Rect);
     method public void getBoundsInScreen(android.graphics.Rect);
@@ -24657,6 +24703,7 @@
     method public android.view.accessibility.AccessibilityNodeInfo getParent();
     method public java.lang.CharSequence getText();
     method public int getWindowId();
+    method public boolean isAccessibilityFocused();
     method public boolean isCheckable();
     method public boolean isChecked();
     method public boolean isClickable();
@@ -24673,6 +24720,7 @@
     method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.accessibility.AccessibilityNodeInfo);
     method public boolean performAction(int);
     method public void recycle();
+    method public void setAccessibilityFocused(boolean);
     method public void setBoundsInParent(android.graphics.Rect);
     method public void setBoundsInScreen(android.graphics.Rect);
     method public void setCheckable(boolean);
@@ -24694,16 +24742,23 @@
     method public void setSource(android.view.View, int);
     method public void setText(java.lang.CharSequence);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ACTION_ACCESSIBILITY_FOCUS = 16; // 0x10
+    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 32; // 0x20
     field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
     field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_CLICK = 64; // 0x40
     field public static final int ACTION_FOCUS = 1; // 0x1
     field public static final int ACTION_SELECT = 4; // 0x4
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+    field public static final int FOCUS_INPUT = 1; // 0x1
   }
 
   public abstract class AccessibilityNodeProvider {
     ctor public AccessibilityNodeProvider();
+    method public android.view.accessibility.AccessibilityNodeInfo accessibilityFocusSearch(int, int);
     method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
+    method public android.view.accessibility.AccessibilityNodeInfo findAccessibilitiyFocus(int);
     method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
     method public boolean performAccessibilityAction(int, int);
   }
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index ddd7f7c..3da35d3 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -19,17 +19,22 @@
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.util.LocaleUtil;
 import android.util.Log;
+import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.internal.os.HandlerCaller;
 
+import java.util.Locale;
+
 /**
  * An accessibility service runs in the background and receives callbacks by the system
  * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
@@ -202,12 +207,65 @@
  * @see android.view.accessibility.AccessibilityManager
  */
 public abstract class AccessibilityService extends Service {
+
+    /**
+     * The user has performed a swipe up gesture on the touch screen.
+     */
+    public static final int GESTURE_SWIPE_UP = 1;
+
+    /**
+     * The user has performed a swipe down gesture on the touch screen.
+     */
+    public static final int GESTURE_SWIPE_DOWN = 2;
+
+    /**
+     * The user has performed a swipe left gesture on the touch screen.
+     */
+    public static final int GESTURE_SWIPE_LEFT = 3;
+
+    /**
+     * The user has performed a swipe right gesture on the touch screen.
+     */
+    public static final int GESTURE_SWIPE_RIGHT = 4;
+
+    /**
+     * The user has performed a swipe left and right gesture on the touch screen.
+     */
+    public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5;
+
+    /**
+     * The user has performed a swipe right and left gesture on the touch screen.
+     */
+    public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6;
+
+    /**
+     * The user has performed a swipe up and down gesture on the touch screen.
+     */
+    public static final int GESTURE_SWIPE_UP_AND_DOWN = 7;
+
+    /**
+     * The user has performed a swipe down and up gesture on the touch screen.
+     */
+    public static final int GESTURE_SWIPE_DOWN_AND_UP = 8;
+
+    /**
+     * The user has performed a clockwise circle gesture on the touch screen.
+     */
+    public static final int GESTURE_CLOCKWISE_CIRCLE = 9;
+
+    /**
+     * The user has performed a counter clockwise circle gesture on the touch screen.
+     */
+    public static final int GESTURE_COUNTER_CLOCKWISE_CIRCLE = 10;
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
     public static final String SERVICE_INTERFACE =
         "android.accessibilityservice.AccessibilityService";
 
+    private static final int UNDEFINED = -1;
+
     /**
      * Name under which an AccessibilityService component publishes information
      * about itself. This meta-data must reference an XML resource containing an
@@ -233,12 +291,15 @@
         public void onInterrupt();
         public void onServiceConnected();
         public void onSetConnectionId(int connectionId);
+        public void onGesture(int gestureId);
     }
 
     private int mConnectionId;
 
     private AccessibilityServiceInfo mInfo;
 
+    private int mLayoutDirection;
+
     /**
      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
      *
@@ -264,6 +325,106 @@
     }
 
     /**
+     * Called by the system when the user performs a specific gesture on the
+     * touch screen.
+     *
+     * @param gestureId The unique id of the performed gesture.
+     *
+     * @see #GESTURE_SWIPE_UP
+     * @see #GESTURE_SWIPE_DOWN
+     * @see #GESTURE_SWIPE_LEFT
+     * @see #GESTURE_SWIPE_RIGHT
+     * @see #GESTURE_SWIPE_UP_AND_DOWN
+     * @see #GESTURE_SWIPE_DOWN_AND_UP
+     * @see #GESTURE_SWIPE_LEFT_AND_RIGHT
+     * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
+     * @see #GESTURE_CLOCKWISE_CIRCLE
+     * @see #GESTURE_COUNTER_CLOCKWISE_CIRCLE
+     */
+    protected void onGesture(int gestureId) {
+        // TODO: Describe the default gesture processing in the javaDoc once it is finalized.
+
+        // Cache the id to avoid locking
+        final int connectionId = mConnectionId;
+        if (connectionId == UNDEFINED) {
+            throw new IllegalStateException("AccessibilityService not connected."
+                    + " Did you receive a call of onServiceConnected()?");
+        }
+        AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByAccessibilityId(connectionId,
+                        AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
+                        AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
+        if (root == null) {
+            return;
+        }
+        AccessibilityNodeInfo current = root.findFocus(View.FOCUS_ACCESSIBILITY);
+        if (current == null) {
+            current = root;
+        }
+        AccessibilityNodeInfo next = null;
+        switch (gestureId) {
+            case GESTURE_SWIPE_UP: {
+                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_OUT);
+            } break;
+            case GESTURE_SWIPE_DOWN: {
+                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_IN);
+            } break;
+            case GESTURE_SWIPE_LEFT: {
+                if (mLayoutDirection == View.LAYOUT_DIRECTION_LTR) {
+                    next = current.focusSearch(View.ACCESSIBILITY_FOCUS_BACKWARD);
+                } else { // LAYOUT_DIRECTION_RTL
+                    next = current.focusSearch(View.ACCESSIBILITY_FOCUS_FORWARD);
+                }
+            } break;
+            case GESTURE_SWIPE_RIGHT: {
+                if (mLayoutDirection == View.LAYOUT_DIRECTION_LTR) {
+                    next = current.focusSearch(View.ACCESSIBILITY_FOCUS_FORWARD);
+                } else { // LAYOUT_DIRECTION_RTL
+                    next = current.focusSearch(View.ACCESSIBILITY_FOCUS_BACKWARD);
+                }
+            } break;
+            case GESTURE_SWIPE_UP_AND_DOWN: {
+                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_UP);
+            } break;
+            case GESTURE_SWIPE_DOWN_AND_UP: {
+                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_DOWN);
+            } break;
+            case GESTURE_SWIPE_LEFT_AND_RIGHT: {
+                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_LEFT);
+            } break;
+            case GESTURE_SWIPE_RIGHT_AND_LEFT: {
+                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_RIGHT);
+            } break;
+        }
+        if (next != null && !next.equals(current)) {
+            next.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+        }
+    }
+
+    /**
+     * Gets the an {@link AccessibilityServiceInfo} describing this
+     * {@link AccessibilityService}. This method is useful if one wants
+     * to change some of the dynamically configurable properties at
+     * runtime.
+     *
+     * @return The accessibility service info.
+     *
+     * @see AccessibilityNodeInfo
+     */
+    public final AccessibilityServiceInfo getServiceInfo() {
+        IAccessibilityServiceConnection connection =
+            AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
+        if (connection != null) {
+            try {
+                return connection.getServiceInfo();
+            } catch (RemoteException re) {
+                Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Sets the {@link AccessibilityServiceInfo} that describes this service.
      * <p>
      * Note: You can call this method any time but the info will be picked up after
@@ -287,19 +448,33 @@
         if (mInfo != null && connection != null) {
             try {
                 connection.setServiceInfo(mInfo);
+                mInfo = null;
+                AccessibilityInteractionClient.getInstance().clearCache();
             } catch (RemoteException re) {
                 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
             }
         }
     }
 
+    @Override
+    public void onCreate() {
+        Locale locale = getResources().getConfiguration().locale;
+        mLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration configuration) {
+        super.onConfigurationChanged(configuration);
+        mLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(configuration.locale);
+    }
+
     /**
      * Implement to return the implementation of the internal accessibility
      * service interface.
      */
     @Override
     public final IBinder onBind(Intent intent) {
-        return new IEventListenerWrapper(this, getMainLooper(), new Callbacks() {
+        return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
             @Override
             public void onServiceConnected() {
                 AccessibilityService.this.onServiceConnected();
@@ -319,14 +494,19 @@
             public void onSetConnectionId( int connectionId) {
                 mConnectionId = connectionId;
             }
+
+            @Override
+            public void onGesture(int gestureId) {
+                AccessibilityService.this.onGesture(gestureId);
+            }
         });
     }
 
     /**
-     * Implements the internal {@link IEventListener} interface to convert
+     * Implements the internal {@link IAccessibilityServiceClient} interface to convert
      * incoming calls to it back to calls on an {@link AccessibilityService}.
      */
-    static class IEventListenerWrapper extends IEventListener.Stub
+    static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
             implements HandlerCaller.Callback {
 
         static final int NO_ID = -1;
@@ -334,12 +514,14 @@
         private static final int DO_SET_SET_CONNECTION = 10;
         private static final int DO_ON_INTERRUPT = 20;
         private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
+        private static final int DO_ON_GESTURE = 40;
 
         private final HandlerCaller mCaller;
 
         private final Callbacks mCallback;
 
-        public IEventListenerWrapper(Context context, Looper looper, Callbacks callback) {
+        public IAccessibilityServiceClientWrapper(Context context, Looper looper,
+                Callbacks callback) {
             mCallback = callback;
             mCaller = new HandlerCaller(context, looper, this);
         }
@@ -360,6 +542,11 @@
             mCaller.sendMessage(message);
         }
 
+        public void onGesture(int gestureId) {
+            Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
+            mCaller.sendMessage(message);
+        }
+
         public void executeMessage(Message message) {
             switch (message.what) {
                 case DO_ON_ACCESSIBILITY_EVENT :
@@ -387,6 +574,10 @@
                         mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID);
                     }
                     return;
+                case DO_ON_GESTURE :
+                    final int gestureId = message.arg1;
+                    mCallback.onGesture(gestureId);
+                    return;
                 default :
                     Log.w(LOG_TAG, "Unknown message type " + message.what);
             }
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 8e53431..e77ed9a 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -25,11 +25,13 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.util.Xml;
+import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -101,6 +103,37 @@
     public static final int DEFAULT = 0x0000001;
 
     /**
+     * If this flag is set the system will regard views that are not important
+     * for accessibility in addition to the ones that are important for accessibility.
+     * That is, views that are marked as not important for accessibility via
+     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} and views that are marked as
+     * potentially important for accessibility via
+     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined
+     * that are not important for accessibility, are both reported while querying the
+     * window content and also the accessibility service will receive accessibility events
+     * from them.
+     * <p>
+     * <strong>Note:</strong> For accessibility services targeting API version
+     * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly
+     * set for the system to regard views that are not important for accessibility. For
+     * accessibility services targeting API version lower than
+     * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are
+     * regarded for accessibility purposes.
+     * </p>
+     * <p>
+     * Usually views not important for accessibility are layout managers that do not
+     * react to user actions, do not draw any content, and do not have any special
+     * semantics in the context of the screen content. For example, a three by three
+     * grid can be implemented as three horizontal linear layouts and one vertical,
+     * or three vertical linear layouts and one horizontal, or one grid layout, etc.
+     * In this context the actual layout mangers used to achieve the grid configuration
+     * are not important, rather it is important that there are nine evenly distributed
+     * elements.
+     * </p>
+     */
+    public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002;
+
+    /**
      * The event types an {@link AccessibilityService} is interested in.
      * <p>
      *   <strong>Can be dynamically set at runtime.</strong>
@@ -165,6 +198,7 @@
      *   <strong>Can be dynamically set at runtime.</strong>
      * </p>
      * @see #DEFAULT
+     * @see #INCLUDE_NOT_IMPORTANT_VIEWS
      */
     public int flags;
 
@@ -561,6 +595,8 @@
         switch (flag) {
             case DEFAULT:
                 return "DEFAULT";
+            case INCLUDE_NOT_IMPORTANT_VIEWS:
+                return "REGARD_VIEWS_NOT_IMPORTANT_FOR_ACCESSIBILITY";
             default:
                 return null;
         }
diff --git a/core/java/android/accessibilityservice/IEventListener.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
similarity index 86%
rename from core/java/android/accessibilityservice/IEventListener.aidl
rename to core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 5536b3c..588728c 100644
--- a/core/java/android/accessibilityservice/IEventListener.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -20,15 +20,17 @@
 import android.view.accessibility.AccessibilityEvent;
 
 /**
- * Top-level interface to accessibility service component (implemented in Service).
+ * Top-level interface to an accessibility service component.
  *
  * @hide
  */
- oneway interface IEventListener {
+ oneway interface IAccessibilityServiceClient {
 
     void setConnection(in IAccessibilityServiceConnection connection, int connectionId);
 
     void onAccessibilityEvent(in AccessibilityEvent event);
 
     void onInterrupt();
+
+    void onGesture(int gestureId);
 }
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 8d17325..30da9db 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -41,13 +41,13 @@
      *     to start from the root.
      * @param interactionId The id of the interaction for matching with the callback result.
      * @param callback Callback which to receive the result.
+     * @param flags Additional flags.
      * @param threadId The id of the calling thread.
-     * @param prefetchFlags flags to guide prefetching.
      * @return The current window scale, where zero means a failure.
      */
     float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
         long accessibilityNodeId, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback, long threadId, int prefetchFlags);
+        IAccessibilityInteractionConnectionCallback callback, int flags, long threadId);
 
     /**
      * Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by View text.
@@ -94,6 +94,48 @@
         long threadId);
 
     /**
+     * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the specified
+     * focus type. The search is performed in the window whose id is specified and starts from
+     * the node whose accessibility id is specified.
+     *
+     * @param accessibilityWindowId A unique window id. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+     *     to query the currently active window.
+     * @param accessibilityNodeId A unique view id or virtual descendant id from
+     *     where to start the search. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+     *     to start from the root.
+     * @param focusType The type of focus to find.
+     * @param interactionId The id of the interaction for matching with the callback result.
+     * @param callback Callback which to receive the result.
+     * @param threadId The id of the calling thread.
+     * @return The current window scale, where zero means a failure.
+     */
+    float findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
+        int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
+
+    /**
+     * Finds an {@link android.view.accessibility.AccessibilityNodeInfo} to take accessibility
+     * focus in the given direction. The search is performed in the window whose id is
+     * specified and starts from the node whose accessibility id is specified.
+     *
+     * @param accessibilityWindowId A unique window id. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+     *     to query the currently active window.
+     * @param accessibilityNodeId A unique view id or virtual descendant id from
+     *     where to start the search. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+     *     to start from the root.
+     * @param direction The direction in which to search for focusable.
+     * @param interactionId The id of the interaction for matching with the callback result.
+     * @param callback Callback which to receive the result.
+     * @param threadId The id of the calling thread.
+     * @return The current window scale, where zero means a failure.
+     */
+    float focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
+        int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
+
+    /**
      * Performs an accessibility action on an
      * {@link android.view.accessibility.AccessibilityNodeInfo}.
      *
@@ -113,4 +155,9 @@
     boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId,
         int action, int interactionId, IAccessibilityInteractionConnectionCallback callback,
         long threadId);
+
+    /**
+     * @return The associated accessibility service info.
+     */
+    AccessibilityServiceInfo getServiceInfo();
 }
diff --git a/core/java/android/accessibilityservice/UiTestAutomationBridge.java b/core/java/android/accessibilityservice/UiTestAutomationBridge.java
index a898c3f..c840bd6 100644
--- a/core/java/android/accessibilityservice/UiTestAutomationBridge.java
+++ b/core/java/android/accessibilityservice/UiTestAutomationBridge.java
@@ -17,7 +17,7 @@
 package android.accessibilityservice;
 
 import android.accessibilityservice.AccessibilityService.Callbacks;
-import android.accessibilityservice.AccessibilityService.IEventListenerWrapper;
+import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper;
 import android.content.Context;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -66,7 +66,7 @@
 
     private volatile int mConnectionId = AccessibilityInteractionClient.NO_ID;
 
-    private IEventListenerWrapper mListener;
+    private IAccessibilityServiceClientWrapper mListener;
 
     private AccessibilityEvent mLastEvent;
 
@@ -133,7 +133,7 @@
         mHandlerThread.start();
         Looper looper = mHandlerThread.getLooper();
 
-        mListener = new IEventListenerWrapper(null, looper, new Callbacks() {
+        mListener = new IAccessibilityServiceClientWrapper(null, looper, new Callbacks() {
             @Override
             public void onServiceConnected() {
                 /* do nothing */
@@ -175,6 +175,11 @@
                     mLock.notifyAll();
                 }
             }
+
+            @Override
+            public void onGesture(int gestureId) {
+                /* do nothing */
+            }
         });
 
         final IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
@@ -252,6 +257,7 @@
     public AccessibilityEvent executeCommandAndWaitForAccessibilityEvent(Runnable command,
             Predicate<AccessibilityEvent> predicate, long timeoutMillis)
             throws TimeoutException, Exception {
+        // TODO: This is broken - remove from here when finalizing this as public APIs.
         synchronized (mLock) {
             // Prepare to wait for an event.
             mWaitingForEventDelivery = true;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 138a88f..0645aa9 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -82,7 +82,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserId;
-import android.os.Vibrator;
+import android.os.SystemVibrator;
 import android.os.storage.StorageManager;
 import android.telephony.TelephonyManager;
 import android.content.ClipboardManager;
@@ -455,7 +455,7 @@
 
         registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
-                    return new Vibrator();
+                    return new SystemVibrator();
                 }});
 
         registerService(WALLPAPER_SERVICE, WALLPAPER_FETCHER);
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index a655dd4..1866830 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -21,7 +21,12 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
 import android.text.TextUtils;
+import android.text.style.URLSpan;
 import android.util.Log;
 
 import java.io.FileInputStream;
@@ -144,6 +149,8 @@
 public class ClipData implements Parcelable {
     static final String[] MIMETYPES_TEXT_PLAIN = new String[] {
         ClipDescription.MIMETYPE_TEXT_PLAIN };
+    static final String[] MIMETYPES_TEXT_HTML = new String[] {
+        ClipDescription.MIMETYPE_TEXT_HTML };
     static final String[] MIMETYPES_TEXT_URILIST = new String[] {
         ClipDescription.MIMETYPE_TEXT_URILIST };
     static final String[] MIMETYPES_TEXT_INTENT = new String[] {
@@ -176,6 +183,7 @@
      */
     public static class Item {
         final CharSequence mText;
+        final String mHtmlText;
         final Intent mIntent;
         final Uri mUri;
 
@@ -184,6 +192,20 @@
          */
         public Item(CharSequence text) {
             mText = text;
+            mHtmlText = null;
+            mIntent = null;
+            mUri = null;
+        }
+
+        /**
+         * Create an Item consisting of a single block of (possibly styled) text,
+         * with an alternative HTML formatted representation.  You <em>must</em>
+         * supply a plain text representation in addition to HTML text; coercion
+         * will not be done from HTML formated text into plain text.
+         */
+        public Item(CharSequence text, String htmlText) {
+            mText = text;
+            mHtmlText = htmlText;
             mIntent = null;
             mUri = null;
         }
@@ -193,6 +215,7 @@
          */
         public Item(Intent intent) {
             mText = null;
+            mHtmlText = null;
             mIntent = intent;
             mUri = null;
         }
@@ -202,16 +225,35 @@
          */
         public Item(Uri uri) {
             mText = null;
+            mHtmlText = null;
             mIntent = null;
             mUri = uri;
         }
 
         /**
          * Create a complex Item, containing multiple representations of
-         * text, intent, and/or URI.
+         * text, Intent, and/or URI.
          */
         public Item(CharSequence text, Intent intent, Uri uri) {
             mText = text;
+            mHtmlText = null;
+            mIntent = intent;
+            mUri = uri;
+        }
+
+        /**
+         * Create a complex Item, containing multiple representations of
+         * text, HTML text, Intent, and/or URI.  If providing HTML text, you
+         * <em>must</em> supply a plain text representation as well; coercion
+         * will not be done from HTML formated text into plain text.
+         */
+        public Item(CharSequence text, String htmlText, Intent intent, Uri uri) {
+            if (htmlText != null && text == null) {
+                throw new IllegalArgumentException(
+                        "Plain text must be supplied if HTML text is supplied");
+            }
+            mText = text;
+            mHtmlText = htmlText;
             mIntent = intent;
             mUri = uri;
         }
@@ -224,6 +266,13 @@
         }
 
         /**
+         * Retrieve the raw HTML text contained in this Item.
+         */
+        public String getHtmlText() {
+            return mHtmlText;
+        }
+
+        /**
          * Retrieve the raw Intent contained in this Item.
          */
         public Intent getIntent() {
@@ -250,7 +299,7 @@
          * the content provider does not supply a text representation, return
          * the raw URI as a string.
          * <li> If {@link #getIntent} is non-null, convert that to an intent:
-         * URI and returnit.
+         * URI and return it.
          * <li> Otherwise, return an empty string.
          * </ul>
          *
@@ -261,12 +310,14 @@
 //BEGIN_INCLUDE(coerceToText)
         public CharSequence coerceToText(Context context) {
             // If this Item has an explicit textual value, simply return that.
-            if (mText != null) {
-                return mText;
+            CharSequence text = getText();
+            if (text != null) {
+                return text;
             }
 
             // If this Item has a URI value, try using that.
-            if (mUri != null) {
+            Uri uri = getUri();
+            if (uri != null) {
 
                 // First see if the URI can be opened as a plain text stream
                 // (of any sub-type).  If so, this is the best textual
@@ -275,7 +326,7 @@
                 try {
                     // Ask for a stream of the desired type.
                     AssetFileDescriptor descr = context.getContentResolver()
-                            .openTypedAssetFileDescriptor(mUri, "text/*", null);
+                            .openTypedAssetFileDescriptor(uri, "text/*", null);
                     stream = descr.createInputStream();
                     InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
 
@@ -308,13 +359,14 @@
 
                 // If we couldn't open the URI as a stream, then the URI itself
                 // probably serves fairly well as a textual representation.
-                return mUri.toString();
+                return uri.toString();
             }
 
             // Finally, if all we have is an Intent, then we can just turn that
             // into text.  Not the most user-friendly thing, but it's something.
-            if (mIntent != null) {
-                return mIntent.toUri(Intent.URI_INTENT_SCHEME);
+            Intent intent = getIntent();
+            if (intent != null) {
+                return intent.toUri(Intent.URI_INTENT_SCHEME);
             }
 
             // Shouldn't get here, but just in case...
@@ -322,6 +374,210 @@
         }
 //END_INCLUDE(coerceToText)
 
+        /**
+         * Like {@link #coerceToHtmlText(Context)}, but any text that would
+         * be returned as HTML formatting will be returned as text with
+         * style spans.
+         * @param context The caller's Context, from which its ContentResolver
+         * and other things can be retrieved.
+         * @return Returns the item's textual representation.
+         */
+        public CharSequence coerceToStyledText(Context context) {
+            CharSequence text = getText();
+            if (text instanceof Spanned) {
+                return text;
+            }
+            String htmlText = getHtmlText();
+            if (htmlText != null) {
+                try {
+                    CharSequence newText = Html.fromHtml(htmlText);
+                    if (newText != null) {
+                        return newText;
+                    }
+                } catch (RuntimeException e) {
+                    // If anything bad happens, we'll fall back on the plain text.
+                }
+            }
+
+            if (text != null) {
+                return text;
+            }
+            return coerceToHtmlOrStyledText(context, true);
+        }
+
+        /**
+         * Turn this item into HTML text, regardless of the type of data it
+         * actually contains.
+         *
+         * <p>The algorithm for deciding what text to return is:
+         * <ul>
+         * <li> If {@link #getHtmlText} is non-null, return that.
+         * <li> If {@link #getText} is non-null, return that, converting to
+         * valid HTML text.  If this text contains style spans,
+         * {@link Html#toHtml(Spanned) Html.toHtml(Spanned)} is used to
+         * convert them to HTML formatting.
+         * <li> If {@link #getUri} is non-null, try to retrieve its data
+         * as a text stream from its content provider.  If the provider can
+         * supply text/html data, that will be preferred and returned as-is.
+         * Otherwise, any text/* data will be returned and escaped to HTML.
+         * If it is not a content: URI or the content provider does not supply
+         * a text representation, HTML text containing a link to the URI
+         * will be returned.
+         * <li> If {@link #getIntent} is non-null, convert that to an intent:
+         * URI and return as an HTML link.
+         * <li> Otherwise, return an empty string.
+         * </ul>
+         *
+         * @param context The caller's Context, from which its ContentResolver
+         * and other things can be retrieved.
+         * @return Returns the item's representation as HTML text.
+         */
+        public String coerceToHtmlText(Context context) {
+            // If the item has an explicit HTML value, simply return that.
+            String htmlText = getHtmlText();
+            if (htmlText != null) {
+                return htmlText;
+            }
+
+            // If this Item has a plain text value, return it as HTML.
+            CharSequence text = getText();
+            if (text != null) {
+                if (text instanceof Spanned) {
+                    return Html.toHtml((Spanned)text);
+                }
+                return Html.escapeHtml(text);
+            }
+
+            text = coerceToHtmlOrStyledText(context, false);
+            return text != null ? text.toString() : null;
+        }
+
+        private CharSequence coerceToHtmlOrStyledText(Context context, boolean styled) {
+            // If this Item has a URI value, try using that.
+            if (mUri != null) {
+
+                // Check to see what data representations the content
+                // provider supports.  We would like HTML text, but if that
+                // is not possible we'll live with plan text.
+                String[] types = context.getContentResolver().getStreamTypes(mUri, "text/*");
+                boolean hasHtml = false;
+                boolean hasText = false;
+                if (types != null) {
+                    for (String type : types) {
+                        if ("text/html".equals(type)) {
+                            hasHtml = true;
+                        } else if (type.startsWith("text/")) {
+                            hasText = true;
+                        }
+                    }
+                }
+
+                // If the provider can serve data we can use, open and load it.
+                if (hasHtml || hasText) {
+                    FileInputStream stream = null;
+                    try {
+                        // Ask for a stream of the desired type.
+                        AssetFileDescriptor descr = context.getContentResolver()
+                                .openTypedAssetFileDescriptor(mUri,
+                                        hasHtml ? "text/html" : "text/plain", null);
+                        stream = descr.createInputStream();
+                        InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
+
+                        // Got it...  copy the stream into a local string and return it.
+                        StringBuilder builder = new StringBuilder(128);
+                        char[] buffer = new char[8192];
+                        int len;
+                        while ((len=reader.read(buffer)) > 0) {
+                            builder.append(buffer, 0, len);
+                        }
+                        String text = builder.toString();
+                        if (hasHtml) {
+                            if (styled) {
+                                // We loaded HTML formatted text and the caller
+                                // want styled text, convert it.
+                                try {
+                                    CharSequence newText = Html.fromHtml(text);
+                                    return newText != null ? newText : text;
+                                } catch (RuntimeException e) {
+                                    return text;
+                                }
+                            } else {
+                                // We loaded HTML formatted text and that is what
+                                // the caller wants, just return it.
+                                return text.toString();
+                            }
+                        }
+                        if (styled) {
+                            // We loaded plain text and the caller wants styled
+                            // text, that is all we have so return it.
+                            return text;
+                        } else {
+                            // We loaded plain text and the caller wants HTML
+                            // text, escape it for HTML.
+                            return Html.escapeHtml(text);
+                        }
+
+                    } catch (FileNotFoundException e) {
+                        // Unable to open content URI as text...  not really an
+                        // error, just something to ignore.
+
+                    } catch (IOException e) {
+                        // Something bad has happened.
+                        Log.w("ClippedData", "Failure loading text", e);
+                        return Html.escapeHtml(e.toString());
+
+                    } finally {
+                        if (stream != null) {
+                            try {
+                                stream.close();
+                            } catch (IOException e) {
+                            }
+                        }
+                    }
+                }
+
+                // If we couldn't open the URI as a stream, then we can build
+                // some HTML text with the URI itself.
+                // probably serves fairly well as a textual representation.
+                if (styled) {
+                    return uriToStyledText(mUri.toString());
+                } else {
+                    return uriToHtml(mUri.toString());
+                }
+            }
+
+            // Finally, if all we have is an Intent, then we can just turn that
+            // into text.  Not the most user-friendly thing, but it's something.
+            if (mIntent != null) {
+                if (styled) {
+                    return uriToStyledText(mIntent.toUri(Intent.URI_INTENT_SCHEME));
+                } else {
+                    return uriToHtml(mIntent.toUri(Intent.URI_INTENT_SCHEME));
+                }
+            }
+
+            // Shouldn't get here, but just in case...
+            return "";
+        }
+
+        private String uriToHtml(String uri) {
+            StringBuilder builder = new StringBuilder(256);
+            builder.append("<a href=\"");
+            builder.append(uri);
+            builder.append("\">");
+            builder.append(Html.escapeHtml(uri));
+            builder.append("</a>");
+            return builder.toString();
+        }
+
+        private CharSequence uriToStyledText(String uri) {
+            SpannableStringBuilder builder = new SpannableStringBuilder();
+            builder.append(uri);
+            builder.setSpan(new URLSpan(uri), 0, builder.length(),
+                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            return builder;
+        }
+
         @Override
         public String toString() {
             StringBuilder b = new StringBuilder(128);
@@ -335,7 +591,10 @@
 
         /** @hide */
         public void toShortString(StringBuilder b) {
-            if (mText != null) {
+            if (mHtmlText != null) {
+                b.append("H:");
+                b.append(mHtmlText);
+            } else if (mText != null) {
                 b.append("T:");
                 b.append(mText);
             } else if (mUri != null) {
@@ -409,6 +668,22 @@
     }
 
     /**
+     * Create a new ClipData holding data of the type
+     * {@link ClipDescription#MIMETYPE_TEXT_HTML}.
+     *
+     * @param label User-visible label for the clip data.
+     * @param text The text of clip as plain text, for receivers that don't
+     * handle HTML.  This is required.
+     * @param htmlText The actual HTML text in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newHtmlText(CharSequence label, CharSequence text,
+            String htmlText) {
+        Item item = new Item(text, htmlText);
+        return new ClipData(label, MIMETYPES_TEXT_HTML, item);
+    }
+
+    /**
      * Create a new ClipData holding an Intent with MIME type
      * {@link ClipDescription#MIMETYPE_TEXT_INTENT}.
      *
@@ -574,6 +849,7 @@
         for (int i=0; i<N; i++) {
             Item item = mItems.get(i);
             TextUtils.writeToParcel(item.mText, dest, flags);
+            dest.writeString(item.mHtmlText);
             if (item.mIntent != null) {
                 dest.writeInt(1);
                 item.mIntent.writeToParcel(dest, flags);
@@ -600,9 +876,10 @@
         final int N = in.readInt();
         for (int i=0; i<N; i++) {
             CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            String htmlText = in.readString();
             Intent intent = in.readInt() != 0 ? Intent.CREATOR.createFromParcel(in) : null;
             Uri uri = in.readInt() != 0 ? Uri.CREATOR.createFromParcel(in) : null;
-            mItems.add(new Item(text, intent, uri));
+            mItems.add(new Item(text, htmlText, intent, uri));
         }
     }
 
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index c6b51ef..5cb6e77 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -41,6 +41,11 @@
     public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
 
     /**
+     * The MIME type for a clip holding HTML text.
+     */
+    public static final String MIMETYPE_TEXT_HTML = "text/html";
+
+    /**
      * The MIME type for a clip holding one or more URIs.  This should be
      * used for URIs that are meaningful to a user (such as an http: URI).
      * It should <em>not</em> be used for a content: URI that references some
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 2930998..722fdc6 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -248,7 +248,7 @@
      * @param mimeTypeFilter The desired MIME type.  This may be a pattern,
      * such as *\/*, to query for all available MIME types that match the
      * pattern.
-     * @return Returns an array of MIME type strings for all availablle
+     * @return Returns an array of MIME type strings for all available
      * data streams that match the given mimeTypeFilter.  If there are none,
      * null is returned.
      */
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 18d682d..19e4372 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -954,7 +954,18 @@
      * using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it
      * should be the MIME type of the data in EXTRA_STREAM.  Use {@literal *}/*
      * if the MIME type is unknown (this will only allow senders that can
-     * handle generic data streams).
+     * handle generic data streams).  If using {@link #EXTRA_TEXT}, you can
+     * also optionally supply {@link #EXTRA_HTML_TEXT} for clients to retrieve
+     * your text with HTML formatting.
+     * <p>
+     * As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, the data
+     * being sent can be supplied through {@link #setClipData(ClipData)}.  This
+     * allows you to use {@link #FLAG_GRANT_READ_URI_PERMISSION} when sharing
+     * content: URIs and other advanced features of {@link ClipData}.  If
+     * using this approach, you still must supply the same data through the
+     * {@link #EXTRA_TEXT} or {@link #EXTRA_STREAM} fields described below
+     * for compatibility with old applications.  If you don't set a ClipData,
+     * it will be copied there for you when calling {@link Context#startActivity(Intent)}.
      * <p>
      * Optional standard extras, which may be interpreted by some recipients as
      * appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC},
@@ -967,11 +978,13 @@
     /**
      * Activity Action: Deliver multiple data to someone else.
      * <p>
-     * Like ACTION_SEND, except the data is multiple.
+     * Like {@link #ACTION_SEND}, except the data is multiple.
      * <p>
      * Input: {@link #getType} is the MIME type of the data being sent.
      * get*ArrayListExtra can have either a {@link #EXTRA_TEXT} or {@link
-     * #EXTRA_STREAM} field, containing the data to be sent.
+     * #EXTRA_STREAM} field, containing the data to be sent.  If using
+     * {@link #EXTRA_TEXT}, you can also optionally supply {@link #EXTRA_HTML_TEXT}
+     * for clients to retrieve your text with HTML formatting.
      * <p>
      * Multiple types are supported, and receivers should handle mixed types
      * whenever possible. The right way for the receiver to check them is to
@@ -983,6 +996,15 @@
      * be image/jpg, but if you are sending image/jpg and image/png, then the
      * intent's type should be image/*.
      * <p>
+     * As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, the data
+     * being sent can be supplied through {@link #setClipData(ClipData)}.  This
+     * allows you to use {@link #FLAG_GRANT_READ_URI_PERMISSION} when sharing
+     * content: URIs and other advanced features of {@link ClipData}.  If
+     * using this approach, you still must supply the same data through the
+     * {@link #EXTRA_TEXT} or {@link #EXTRA_STREAM} fields described below
+     * for compatibility with old applications.  If you don't set a ClipData,
+     * it will be copied there for you when calling {@link Context#startActivity(Intent)}.
+     * <p>
      * Optional standard extras, which may be interpreted by some recipients as
      * appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC},
      * {@link #EXTRA_BCC}, {@link #EXTRA_SUBJECT}.
@@ -2501,6 +2523,14 @@
     public static final String EXTRA_TEXT = "android.intent.extra.TEXT";
 
     /**
+     * A constant String that is associated with the Intent, used with
+     * {@link #ACTION_SEND} to supply an alternative to {@link #EXTRA_TEXT}
+     * as HTML formatted text.  Note that you <em>must</em> also supply
+     * {@link #EXTRA_TEXT}.
+     */
+    public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+
+    /**
      * A content: URI holding a stream of data associated with the Intent,
      * used with {@link #ACTION_SEND} to supply the data being sent.
      */
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index ca8321f..3137947 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -18,6 +18,7 @@
 
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.IInputDevicesChangedListener;
+import android.os.IBinder;
 import android.view.InputDevice;
 import android.view.InputEvent;
 
@@ -46,4 +47,8 @@
 
     // Registers an input devices changed listener.
     void registerInputDevicesChangedListener(IInputDevicesChangedListener listener);
+
+    // Input device vibrator control.
+    void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token);
+    void cancelVibrate(int deviceId, IBinder token);
 }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 35c49a1..b39b823 100755
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -19,12 +19,14 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.Context;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.Vibrator;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Log;
@@ -587,6 +589,15 @@
     }
 
     /**
+     * Gets a vibrator service associated with an input device, assuming it has one.
+     * @return The vibrator, never null.
+     * @hide
+     */
+    public Vibrator getInputDeviceVibrator(int deviceId) {
+        return new InputDeviceVibrator(deviceId);
+    }
+
+    /**
      * Listens for changes in input devices.
      */
     public interface InputDeviceListener {
@@ -645,4 +656,45 @@
             }
         }
     }
+
+    private final class InputDeviceVibrator extends Vibrator {
+        private final int mDeviceId;
+        private final Binder mToken;
+
+        public InputDeviceVibrator(int deviceId) {
+            mDeviceId = deviceId;
+            mToken = new Binder();
+        }
+
+        @Override
+        public boolean hasVibrator() {
+            return true;
+        }
+
+        @Override
+        public void vibrate(long milliseconds) {
+            vibrate(new long[] { 0, milliseconds}, -1);
+        }
+
+        @Override
+        public void vibrate(long[] pattern, int repeat) {
+            if (repeat >= pattern.length) {
+                throw new ArrayIndexOutOfBoundsException();
+            }
+            try {
+                mIm.vibrate(mDeviceId, pattern, repeat, mToken);
+            } catch (RemoteException ex) {
+                Log.w(TAG, "Failed to vibrate.", ex);
+            }
+        }
+
+        @Override
+        public void cancel() {
+            try {
+                mIm.cancelVibrate(mDeviceId, mToken);
+            } catch (RemoteException ex) {
+                Log.w(TAG, "Failed to cancel vibration.", ex);
+            }
+        }
+    }
 }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 89c9c36..35e8e47 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -32,6 +32,7 @@
     /** Control UID policies. */
     void setAppPolicy(int appId, int policy);
     int getAppPolicy(int appId);
+    int[] getAppsWithPolicy(int policy);
 
     boolean isUidForeground(int uid);
 
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 2b36131..07bfd4b 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -92,6 +92,14 @@
         }
     }
 
+    public int[] getAppsWithPolicy(int policy) {
+        try {
+            return mService.getAppsWithPolicy(policy);
+        } catch (RemoteException e) {
+            return new int[0];
+        }
+    }
+
     public void registerListener(INetworkPolicyListener listener) {
         try {
             mService.registerListener(listener);
diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java
new file mode 100644
index 0000000..8de4e06
--- /dev/null
+++ b/core/java/android/os/NullVibrator.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+/**
+ * Vibrator implementation that does nothing.
+ *
+ * @hide
+ */
+public class NullVibrator extends Vibrator {
+    private static final NullVibrator sInstance = new NullVibrator();
+
+    private NullVibrator() {
+    }
+
+    public static NullVibrator getInstance() {
+        return sInstance;
+    }
+
+    @Override
+    public boolean hasVibrator() {
+        return false;
+    }
+
+    @Override
+    public void vibrate(long milliseconds) {
+    }
+
+    @Override
+    public void vibrate(long[] pattern, int repeat) {
+        if (repeat >= pattern.length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    @Override
+    public void cancel() {
+    }
+}
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
new file mode 100644
index 0000000..7c5a47e
--- /dev/null
+++ b/core/java/android/os/SystemVibrator.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+/**
+ * Vibrator implementation that controls the main system vibrator.
+ *
+ * @hide
+ */
+public class SystemVibrator extends Vibrator {
+    private static final String TAG = "Vibrator";
+
+    private final IVibratorService mService;
+    private final Binder mToken = new Binder();
+
+    public SystemVibrator() {
+        mService = IVibratorService.Stub.asInterface(
+                ServiceManager.getService("vibrator"));
+    }
+
+    @Override
+    public boolean hasVibrator() {
+        if (mService == null) {
+            Log.w(TAG, "Failed to vibrate; no vibrator service.");
+            return false;
+        }
+        try {
+            return mService.hasVibrator();
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    @Override
+    public void vibrate(long milliseconds) {
+        if (mService == null) {
+            Log.w(TAG, "Failed to vibrate; no vibrator service.");
+            return;
+        }
+        try {
+            mService.vibrate(milliseconds, mToken);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to vibrate.", e);
+        }
+    }
+
+    @Override
+    public void vibrate(long[] pattern, int repeat) {
+        if (mService == null) {
+            Log.w(TAG, "Failed to vibrate; no vibrator service.");
+            return;
+        }
+        // catch this here because the server will do nothing.  pattern may
+        // not be null, let that be checked, because the server will drop it
+        // anyway
+        if (repeat < pattern.length) {
+            try {
+                mService.vibratePattern(pattern, repeat, mToken);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to vibrate.", e);
+            }
+        } else {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    @Override
+    public void cancel() {
+        if (mService == null) {
+            return;
+        }
+        try {
+            mService.cancelVibrate(mToken);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to cancel vibration.", e);
+        }
+    }
+}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 3769cfe..3f783c9 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -16,61 +16,37 @@
 
 package android.os;
 
-import android.util.Log;
+import android.content.Context;
 
 /**
  * Class that operates the vibrator on the device.
  * <p>
  * If your process exits, any vibration you started with will stop.
  * </p>
+ *
+ * To obtain an instance of the system vibrator, call
+ * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as argument.
  */
-public class Vibrator
-{
-    private static final String TAG = "Vibrator";
-
-    IVibratorService mService;
-    private final Binder mToken = new Binder();
-
-    /** @hide */
-    public Vibrator()
-    {
-        mService = IVibratorService.Stub.asInterface(
-                ServiceManager.getService("vibrator"));
+public abstract class Vibrator {
+    /**
+     * @hide to prevent subclassing from outside of the framework
+     */
+    public Vibrator() {
     }
 
     /**
-     * Check whether the hardware has a vibrator.  Returns true if a vibrator
-     * exists, else false.
+     * Check whether the hardware has a vibrator.
+     *
+     * @return True if the hardware has a vibrator, else false.
      */
-    public boolean hasVibrator() {
-        if (mService == null) {
-            Log.w(TAG, "Failed to vibrate; no vibrator service.");
-            return false;
-        }
-        try {
-            return mService.hasVibrator();
-        } catch (RemoteException e) {
-        }
-        return false;
-    }
+    public abstract boolean hasVibrator();
     
     /**
-     * Turn the vibrator on.
+     * Vibrate constantly for the specified period of time.
      *
      * @param milliseconds The number of milliseconds to vibrate.
      */
-    public void vibrate(long milliseconds)
-    {
-        if (mService == null) {
-            Log.w(TAG, "Failed to vibrate; no vibrator service.");
-            return;
-        }
-        try {
-            mService.vibrate(milliseconds, mToken);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed to vibrate.", e);
-        }
-    }
+    public abstract void vibrate(long milliseconds);
 
     /**
      * Vibrate with a given pattern.
@@ -90,38 +66,10 @@
      * @param repeat the index into pattern at which to repeat, or -1 if
      *        you don't want to repeat.
      */
-    public void vibrate(long[] pattern, int repeat)
-    {
-        if (mService == null) {
-            Log.w(TAG, "Failed to vibrate; no vibrator service.");
-            return;
-        }
-        // catch this here because the server will do nothing.  pattern may
-        // not be null, let that be checked, because the server will drop it
-        // anyway
-        if (repeat < pattern.length) {
-            try {
-                mService.vibratePattern(pattern, repeat, mToken);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to vibrate.", e);
-            }
-        } else {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-    }
+    public abstract void vibrate(long[] pattern, int repeat);
 
     /**
      * Turn the vibrator off.
      */
-    public void cancel()
-    {
-        if (mService == null) {
-            return;
-        }
-        try {
-            mService.cancelVibrate(mToken);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed to cancel vibration.", e);
-        }
-    }
+    public abstract void cancel();
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2aaf548..6dfbb2f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1464,6 +1464,20 @@
         public static final String VIBRATE_ON = "vibrate_on";
 
         /**
+         * If 1, redirects the system vibrator to all currently attached input devices
+         * that support vibration.  If there are no such input devices, then the system
+         * vibrator is used instead.
+         * If 0, does not register the system vibrator.
+         *
+         * This setting is mainly intended to provide a compatibility mechanism for
+         * applications that only know about the system vibrator and do not use the
+         * input device vibrator API.
+         *
+         * @hide
+         */
+        public static final String VIBRATE_INPUT_DEVICES = "vibrate_input_devices";
+
+        /**
          * Ringer volume. This is used internally, changing this value will not
          * change the volume. See AudioManager.
          */
@@ -1970,6 +1984,7 @@
             SCREEN_BRIGHTNESS_MODE,
             SCREEN_AUTO_BRIGHTNESS_ADJ,
             VIBRATE_ON,
+            VIBRATE_INPUT_DEVICES,
             MODE_RINGER,
             MODE_RINGER_STREAMS_AFFECTED,
             MUTE_STREAMS_AFFECTED,
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 8c97293..35e2e4a 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -147,6 +147,15 @@
         return out.toString();
     }
 
+    /**
+     * Returns an HTML escaped representation of the given plain text.
+     */
+    public static String escapeHtml(CharSequence text) {
+        StringBuilder out = new StringBuilder();
+        withinStyle(out, text, 0, text.length());
+        return out.toString();
+    }
+
     private static void withinHtml(StringBuilder out, Spanned text) {
         int len = text.length();
 
@@ -370,7 +379,7 @@
         }
     }
 
-    private static void withinStyle(StringBuilder out, Spanned text,
+    private static void withinStyle(StringBuilder out, CharSequence text,
                                     int start, int end) {
         for (int i = start; i < end; i++) {
             char c = text.charAt(i);
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
new file mode 100644
index 0000000..ab21b32
--- /dev/null
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -0,0 +1,900 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.accessibility.AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Pool;
+import android.util.Poolable;
+import android.util.PoolableManager;
+import android.util.Pools;
+import android.util.SparseLongArray;
+import android.view.ViewGroup.ChildListForAccessibility;
+import android.view.accessibility.AccessibilityInteractionClient;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Class for managing accessibility interactions initiated from the system
+ * and targeting the view hierarchy. A *ClientThread method is to be
+ * called from the interaction connection ViewAncestor gives the system to
+ * talk to it and a corresponding *UiThread method that is executed on the
+ * UI thread.
+ */
+final class AccessibilityInteractionController {
+    private static final int POOL_SIZE = 5;
+
+    private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
+        new ArrayList<AccessibilityNodeInfo>();
+
+    private final Handler mHandler = new PrivateHandler();
+
+    private final ViewRootImpl mViewRootImpl;
+
+    private final AccessibilityNodePrefetcher mPrefetcher;
+
+    public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
+        mViewRootImpl = viewRootImpl;
+        mPrefetcher = new AccessibilityNodePrefetcher();
+    }
+
+    // Reusable poolable arguments for interacting with the view hierarchy
+    // to fit more arguments than Message and to avoid sharing objects between
+    // two messages since several threads can send messages concurrently.
+    private final Pool<SomeArgs> mPool = Pools.synchronizedPool(Pools.finitePool(
+            new PoolableManager<SomeArgs>() {
+                public SomeArgs newInstance() {
+                    return new SomeArgs();
+                }
+
+                public void onAcquired(SomeArgs info) {
+                    /* do nothing */
+                }
+
+                public void onReleased(SomeArgs info) {
+                    info.clear();
+                }
+            }, POOL_SIZE)
+    );
+
+    private class SomeArgs implements Poolable<SomeArgs> {
+        private SomeArgs mNext;
+        private boolean mIsPooled;
+
+        public Object arg1;
+        public Object arg2;
+        public int argi1;
+        public int argi2;
+        public int argi3;
+
+        public SomeArgs getNextPoolable() {
+            return mNext;
+        }
+
+        public boolean isPooled() {
+            return mIsPooled;
+        }
+
+        public void setNextPoolable(SomeArgs args) {
+            mNext = args;
+        }
+
+        public void setPooled(boolean isPooled) {
+            mIsPooled = isPooled;
+        }
+
+        private void clear() {
+            arg1 = null;
+            arg2 = null;
+            argi1 = 0;
+            argi2 = 0;
+            argi3 = 0;
+        }
+    }
+
+    public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
+            long accessibilityNodeId, int interactionId,
+            IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
+            long interrogatingTid) {
+        Message message = mHandler.obtainMessage();
+        message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
+        message.arg1 = flags;
+        SomeArgs args = mPool.acquire();
+        args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+        args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+        args.argi3 = interactionId;
+        args.arg1 = callback;
+        message.obj = args;
+        // If the interrogation is performed by the same thread as the main UI
+        // thread in this process, set the message as a static reference so
+        // after this call completes the same thread but in the interrogating
+        // client can handle the message to generate the result.
+        if (interrogatingPid == Process.myPid()
+                && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+            AccessibilityInteractionClient.getInstanceForThread(
+                    interrogatingTid).setSameThreadMessage(message);
+        } else {
+            mHandler.sendMessage(message);
+        }
+    }
+
+    private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
+        final int flags = message.arg1;
+        SomeArgs args = (SomeArgs) message.obj;
+        final int accessibilityViewId = args.argi1;
+        final int virtualDescendantId = args.argi2;
+        final int interactionId = args.argi3;
+        final IAccessibilityInteractionConnectionCallback callback =
+            (IAccessibilityInteractionConnectionCallback) args.arg1;
+        mPool.release(args);
+        List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
+        infos.clear();
+        try {
+            if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+                return;
+            }
+            mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+                (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+            View root = null;
+            if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) {
+                root = mViewRootImpl.mView;
+            } else {
+                root = findViewByAccessibilityId(accessibilityViewId);
+            }
+            if (root != null && isDisplayedOnScreen(root)) {
+                mPrefetcher.prefetchAccessibilityNodeInfos(root, virtualDescendantId, flags, infos);
+            }
+        } finally {
+            try {
+                mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+                callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
+                infos.clear();
+            } catch (RemoteException re) {
+                /* ignore - the other side will time out */
+            }
+        }
+    }
+
+    public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNodeId,
+            int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
+            int flags, int interrogatingPid, long interrogatingTid) {
+        Message message = mHandler.obtainMessage();
+        message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
+        message.arg1 = flags;
+        message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+        SomeArgs args = mPool.acquire();
+        args.argi1 = viewId;
+        args.argi2 = interactionId;
+        args.arg1 = callback;
+        message.obj = args;
+        // If the interrogation is performed by the same thread as the main UI
+        // thread in this process, set the message as a static reference so
+        // after this call completes the same thread but in the interrogating
+        // client can handle the message to generate the result.
+        if (interrogatingPid == Process.myPid()
+                && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+            AccessibilityInteractionClient.getInstanceForThread(
+                    interrogatingTid).setSameThreadMessage(message);
+        } else {
+            mHandler.sendMessage(message);
+        }
+    }
+
+    private void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
+        final int flags = message.arg1;
+        final int accessibilityViewId = message.arg2;
+        SomeArgs args = (SomeArgs) message.obj;
+        final int viewId = args.argi1;
+        final int interactionId = args.argi2;
+        final IAccessibilityInteractionConnectionCallback callback =
+            (IAccessibilityInteractionConnectionCallback) args.arg1;
+        mPool.release(args);
+        AccessibilityNodeInfo info = null;
+        try {
+            if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+                return;
+            }
+            mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+                (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+            View root = null;
+            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+                root = findViewByAccessibilityId(accessibilityViewId);
+            } else {
+                root = mViewRootImpl.mView;
+            }
+            if (root != null) {
+                View target = root.findViewById(viewId);
+                if (target != null && isDisplayedOnScreen(target)) {
+                    info = target.createAccessibilityNodeInfo();
+                }
+            }
+        } finally {
+            try {
+                mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+                callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+            } catch (RemoteException re) {
+                /* ignore - the other side will time out */
+            }
+        }
+    }
+
+    public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
+            String text, int interactionId, IAccessibilityInteractionConnectionCallback callback,
+            int flags,  int interrogatingPid, long interrogatingTid) {
+        Message message = mHandler.obtainMessage();
+        message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT;
+        message.arg1 = flags;
+        SomeArgs args = mPool.acquire();
+        args.arg1 = text;
+        args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+        args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+        args.argi3 = interactionId;
+        args.arg2 = callback;
+        message.obj = args;
+        // If the interrogation is performed by the same thread as the main UI
+        // thread in this process, set the message as a static reference so
+        // after this call completes the same thread but in the interrogating
+        // client can handle the message to generate the result.
+        if (interrogatingPid == Process.myPid()
+                && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+            AccessibilityInteractionClient.getInstanceForThread(
+                    interrogatingTid).setSameThreadMessage(message);
+        } else {
+            mHandler.sendMessage(message);
+        }
+    }
+
+    private void findAccessibilityNodeInfosByTextUiThread(Message message) {
+        final int flags = message.arg1;
+        SomeArgs args = (SomeArgs) message.obj;
+        final String text = (String) args.arg1;
+        final int accessibilityViewId = args.argi1;
+        final int virtualDescendantId = args.argi2;
+        final int interactionId = args.argi3;
+        final IAccessibilityInteractionConnectionCallback callback =
+            (IAccessibilityInteractionConnectionCallback) args.arg2;
+        mPool.release(args);
+        List<AccessibilityNodeInfo> infos = null;
+        try {
+            if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+                return;
+            }
+            mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+                (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+            View root = null;
+            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+                root = findViewByAccessibilityId(accessibilityViewId);
+            } else {
+                root = mViewRootImpl.mView;
+            }
+            if (root != null && isDisplayedOnScreen(root)) {
+                AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
+                if (provider != null) {
+                    infos = provider.findAccessibilityNodeInfosByText(text,
+                            virtualDescendantId);
+                } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
+                    ArrayList<View> foundViews = mViewRootImpl.mAttachInfo.mTempArrayList;
+                    foundViews.clear();
+                    root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
+                            | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
+                            | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
+                    if (!foundViews.isEmpty()) {
+                        infos = mTempAccessibilityNodeInfoList;
+                        infos.clear();
+                        final int viewCount = foundViews.size();
+                        for (int i = 0; i < viewCount; i++) {
+                            View foundView = foundViews.get(i);
+                            if (isDisplayedOnScreen(foundView)) {
+                                provider = foundView.getAccessibilityNodeProvider();
+                                if (provider != null) {
+                                    List<AccessibilityNodeInfo> infosFromProvider =
+                                        provider.findAccessibilityNodeInfosByText(text,
+                                                virtualDescendantId);
+                                    if (infosFromProvider != null) {
+                                        infos.addAll(infosFromProvider);
+                                    }
+                                } else  {
+                                    infos.add(foundView.createAccessibilityNodeInfo());
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } finally {
+            try {
+                mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+                callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
+            } catch (RemoteException re) {
+                /* ignore - the other side will time out */
+            }
+        }
+    }
+
+    public void findFocusClientThread(long accessibilityNodeId, int interactionId, int focusType,
+            IAccessibilityInteractionConnectionCallback callback,  int flags, int interogatingPid,
+            long interrogatingTid) {
+        Message message = mHandler.obtainMessage();
+        message.what = PrivateHandler.MSG_FIND_FOCUS;
+        message.arg1 = flags;
+        message.arg2 = focusType;
+        SomeArgs args = mPool.acquire();
+        args.argi1 = interactionId;
+        args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+        args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+        args.arg1 = callback;
+        message.obj = args;
+        // If the interrogation is performed by the same thread as the main UI
+        // thread in this process, set the message as a static reference so
+        // after this call completes the same thread but in the interrogating
+        // client can handle the message to generate the result.
+        if (interogatingPid == Process.myPid()
+                && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+            AccessibilityInteractionClient.getInstanceForThread(
+                    interrogatingTid).setSameThreadMessage(message);
+        } else {
+            mHandler.sendMessage(message);
+        }
+    }
+
+    private void findFocusUiThread(Message message) {
+        final int flags = message.arg1;
+        final int focusType = message.arg2;
+        SomeArgs args = (SomeArgs) message.obj;
+        final int interactionId = args.argi1;
+        final int accessibilityViewId = args.argi2;
+        final int virtualDescendantId = args.argi3;
+        final IAccessibilityInteractionConnectionCallback callback =
+            (IAccessibilityInteractionConnectionCallback) args.arg1;
+        mPool.release(args);
+        AccessibilityNodeInfo focused = null;
+        try {
+            if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+                return;
+            }
+            mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+                (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+            View root = null;
+            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+                root = findViewByAccessibilityId(accessibilityViewId);
+            } else {
+                root = mViewRootImpl.mView;
+            }
+            if (root != null && isDisplayedOnScreen(root)) {
+                switch (focusType) {
+                    case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
+                        View host = mViewRootImpl.mAccessibilityFocusedHost;
+                        // If there is no accessibility focus host or it is not a descendant
+                        // of the root from which to start the search, then the search failed.
+                        if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
+                            break;
+                        }
+                        // If the host has a provider ask this provider to search for the
+                        // focus instead fetching all provider nodes to do the search here.
+                        AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
+                        if (provider != null) {
+                            focused = provider.findAccessibilitiyFocus(virtualDescendantId);
+                        } else if (virtualDescendantId == View.NO_ID) {
+                            focused = host.createAccessibilityNodeInfo();
+                        }
+                    } break;
+                    case AccessibilityNodeInfo.FOCUS_INPUT: {
+                        // Input focus cannot go to virtual views.
+                        View target = root.findFocus();
+                        if (target != null && isDisplayedOnScreen(target)) {
+                            focused = target.createAccessibilityNodeInfo();
+                        }
+                    } break;
+                    default:
+                        throw new IllegalArgumentException("Unknown focus type: " + focusType);
+                }
+            }
+        } finally {
+            try {
+                mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+                callback.setFindAccessibilityNodeInfoResult(focused, interactionId);
+            } catch (RemoteException re) {
+                /* ignore - the other side will time out */
+            }
+        }
+    }
+
+    public void focusSearchClientThread(long accessibilityNodeId, int interactionId, int direction,
+            IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
+            long interrogatingTid) {
+        Message message = mHandler.obtainMessage();
+        message.what = PrivateHandler.MSG_FOCUS_SEARCH;
+        message.arg1 = flags;
+        message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+        SomeArgs args = mPool.acquire();
+        args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+        args.argi2 = direction;
+        args.argi3 = interactionId;
+        args.arg1 = callback;
+        message.obj = args;
+        // If the interrogation is performed by the same thread as the main UI
+        // thread in this process, set the message as a static reference so
+        // after this call completes the same thread but in the interrogating
+        // client can handle the message to generate the result.
+        if (interogatingPid == Process.myPid()
+                && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+            AccessibilityInteractionClient.getInstanceForThread(
+                    interrogatingTid).setSameThreadMessage(message);
+        } else {
+            mHandler.sendMessage(message);
+        }
+    }
+
+    private void focusSearchUiThread(Message message) {
+        final int flags = message.arg1;
+        final int accessibilityViewId = message.arg2;
+        SomeArgs args = (SomeArgs) message.obj;
+        final int virtualDescendantId = args.argi1;
+        final int direction = args.argi2;
+        final int interactionId = args.argi3;
+        final IAccessibilityInteractionConnectionCallback callback =
+            (IAccessibilityInteractionConnectionCallback) args.arg1;
+        mPool.release(args);
+        AccessibilityNodeInfo next = null;
+        try {
+            if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+                return;
+            }
+            mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+                (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+            View root = null;
+            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+                root = findViewByAccessibilityId(accessibilityViewId);
+            } else {
+                root = mViewRootImpl.mView;
+            }
+            if (root != null && isDisplayedOnScreen(root)) {
+                if ((direction & View.FOCUS_ACCESSIBILITY) ==  View.FOCUS_ACCESSIBILITY) {
+                    AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
+                    if (provider != null) {
+                        next = provider.accessibilityFocusSearch(direction,
+                                virtualDescendantId);
+                    } else if (virtualDescendantId == View.NO_ID) {
+                        View nextView = root.focusSearch(direction);
+                        if (nextView != null) {
+                            // If the focus search reached a node with a provider
+                            // we delegate to the provider to find the next one.
+                            provider = nextView.getAccessibilityNodeProvider();
+                            if (provider != null) {
+                                next = provider.accessibilityFocusSearch(direction,
+                                        virtualDescendantId);
+                            } else {
+                                next = nextView.createAccessibilityNodeInfo();
+                             }
+                        }
+                    }
+                } else {
+                    View nextView = root.focusSearch(direction);
+                    if (nextView != null) {
+                        next = nextView.createAccessibilityNodeInfo();
+                    }
+                }
+            }
+        } finally {
+            try {
+                mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+                callback.setFindAccessibilityNodeInfoResult(next, interactionId);
+            } catch (RemoteException re) {
+                /* ignore - the other side will time out */
+            }
+        }
+    }
+
+    public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
+            int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+            int interogatingPid, long interrogatingTid) {
+        Message message = mHandler.obtainMessage();
+        message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
+        message.arg1 = flags;
+        message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+        SomeArgs args = mPool.acquire();
+        args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+        args.argi2 = action;
+        args.argi3 = interactionId;
+        args.arg1 = callback;
+        message.obj = args;
+        // If the interrogation is performed by the same thread as the main UI
+        // thread in this process, set the message as a static reference so
+        // after this call completes the same thread but in the interrogating
+        // client can handle the message to generate the result.
+        if (interogatingPid == Process.myPid()
+                && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+            AccessibilityInteractionClient.getInstanceForThread(
+                    interrogatingTid).setSameThreadMessage(message);
+        } else {
+            mHandler.sendMessage(message);
+        }
+    }
+
+    private void perfromAccessibilityActionUiThread(Message message) {
+        final int flags = message.arg1;
+        final int accessibilityViewId = message.arg2;
+        SomeArgs args = (SomeArgs) message.obj;
+        final int virtualDescendantId = args.argi1;
+        final int action = args.argi2;
+        final int interactionId = args.argi3;
+        final IAccessibilityInteractionConnectionCallback callback =
+            (IAccessibilityInteractionConnectionCallback) args.arg1;
+        mPool.release(args);
+        boolean succeeded = false;
+        try {
+            if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+                return;
+            }
+            mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+                (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+            View target = null;
+            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+                target = findViewByAccessibilityId(accessibilityViewId);
+            } else {
+                target = mViewRootImpl.mView;
+            }
+            if (target != null && isDisplayedOnScreen(target)) {
+                AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
+                if (provider != null) {
+                    succeeded = provider.performAccessibilityAction(action, virtualDescendantId);
+                } else if (virtualDescendantId == View.NO_ID) {
+                    succeeded = target.performAccessibilityAction(action);
+                }
+            }
+        } finally {
+            try {
+                mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+                callback.setPerformAccessibilityActionResult(succeeded, interactionId);
+            } catch (RemoteException re) {
+                /* ignore - the other side will time out */
+            }
+        }
+    }
+
+    private View findViewByAccessibilityId(int accessibilityId) {
+        View root = mViewRootImpl.mView;
+        if (root == null) {
+            return null;
+        }
+        View foundView = root.findViewByAccessibilityId(accessibilityId);
+        if (foundView != null && !isDisplayedOnScreen(foundView)) {
+            return null;
+        }
+        return foundView;
+    }
+
+    /**
+     * Computes whether a view is visible on the screen.
+     *
+     * @param view The view to check.
+     * @return Whether the view is visible on the screen.
+     */
+    private boolean isDisplayedOnScreen(View view) {
+        // The first two checks are made also made by isShown() which
+        // however traverses the tree up to the parent to catch that.
+        // Therefore, we do some fail fast check to minimize the up
+        // tree traversal.
+        return (view.mAttachInfo != null
+                && view.mAttachInfo.mWindowVisibility == View.VISIBLE
+                && view.isShown()
+                && view.getGlobalVisibleRect(mViewRootImpl.mTempRect));
+    }
+
+    /**
+     * This class encapsulates a prefetching strategy for the accessibility APIs for
+     * querying window content. It is responsible to prefetch a batch of
+     * AccessibilityNodeInfos in addition to the one for a requested node.
+     */
+    private class AccessibilityNodePrefetcher {
+
+        private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
+
+        public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int prefetchFlags,
+                List<AccessibilityNodeInfo> outInfos) {
+            AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+            if (provider == null) {
+                AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
+                if (root != null) {
+                    outInfos.add(root);
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+                        prefetchPredecessorsOfRealNode(view, outInfos);
+                    }
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+                        prefetchSiblingsOfRealNode(view, outInfos);
+                    }
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+                        prefetchDescendantsOfRealNode(view, outInfos);
+                    }
+                }
+            } else {
+                AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId);
+                if (root != null) {
+                    outInfos.add(root);
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+                        prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
+                    }
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+                        prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
+                    }
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+                        prefetchDescendantsOfVirtualNode(root, provider, outInfos);
+                    }
+                }
+            }
+        }
+
+        private void prefetchPredecessorsOfRealNode(View view,
+                List<AccessibilityNodeInfo> outInfos) {
+            ViewParent parent = view.getParentForAccessibility();
+            while (parent instanceof View
+                    && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                View parentView = (View) parent;
+                final long parentNodeId = AccessibilityNodeInfo.makeNodeId(
+                        parentView.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
+                AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
+                if (info != null) {
+                    outInfos.add(info);
+                }
+                parent = parent.getParentForAccessibility();
+            }
+        }
+
+        private void prefetchSiblingsOfRealNode(View current,
+                List<AccessibilityNodeInfo> outInfos) {
+            ViewParent parent = current.getParentForAccessibility();
+            if (parent instanceof ViewGroup) {
+                ViewGroup parentGroup = (ViewGroup) parent;
+                ChildListForAccessibility children = ChildListForAccessibility.obtain(parentGroup,
+                        false);
+                final int childCount = children.getChildCount();
+                for (int i = 0; i < childCount; i++) {
+                    if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                        children.recycle();
+                        return;
+                    }
+                    View child = children.getChildAt(i);
+                    if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
+                            &&  isDisplayedOnScreen(child)) {
+                        AccessibilityNodeInfo info = null;
+                        AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
+                        if (provider == null) {
+                            info = child.createAccessibilityNodeInfo();
+                        } else {
+                            info = provider.createAccessibilityNodeInfo(
+                                    AccessibilityNodeInfo.UNDEFINED);
+                        }
+                        if (info != null) {
+                            outInfos.add(info);
+                        }
+                    }
+                }
+                children.recycle();
+            }
+        }
+
+        private void prefetchDescendantsOfRealNode(View root,
+                List<AccessibilityNodeInfo> outInfos) {
+            if (!(root instanceof ViewGroup)) {
+                return;
+            }
+            ViewGroup rootGroup = (ViewGroup) root;
+            HashMap<View, AccessibilityNodeInfo> addedChildren =
+                new HashMap<View, AccessibilityNodeInfo>();
+            ChildListForAccessibility children = ChildListForAccessibility.obtain(rootGroup, false);
+            final int childCount = children.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                    children.recycle();
+                    return;
+                }
+                View child = children.getChildAt(i);
+                if ( isDisplayedOnScreen(child)) {
+                    AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
+                    if (provider == null) {
+                        AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
+                        if (info != null) {
+                            outInfos.add(info);
+                            addedChildren.put(child, null);
+                        }
+                    } else {
+                        AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
+                               AccessibilityNodeInfo.UNDEFINED);
+                        if (info != null) {
+                            outInfos.add(info);
+                            addedChildren.put(child, info);
+                        }
+                    }
+                }
+            }
+            children.recycle();
+            if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
+                    View addedChild = entry.getKey();
+                    AccessibilityNodeInfo virtualRoot = entry.getValue();
+                    if (virtualRoot == null) {
+                        prefetchDescendantsOfRealNode(addedChild, outInfos);
+                    } else {
+                        AccessibilityNodeProvider provider =
+                            addedChild.getAccessibilityNodeProvider();
+                        prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
+                    }
+                }
+            }
+        }
+
+        private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
+                View providerHost, AccessibilityNodeProvider provider,
+                List<AccessibilityNodeInfo> outInfos) {
+            long parentNodeId = root.getParentNodeId();
+            int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
+            while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+                if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                    return;
+                }
+                final int virtualDescendantId =
+                    AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
+                if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+                        || accessibilityViewId == providerHost.getAccessibilityViewId()) {
+                    AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
+                            virtualDescendantId);
+                    if (parent != null) {
+                        outInfos.add(parent);
+                    }
+                    parentNodeId = parent.getParentNodeId();
+                    accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
+                            parentNodeId);
+                } else {
+                    prefetchPredecessorsOfRealNode(providerHost, outInfos);
+                    return;
+                }
+            }
+        }
+
+        private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
+                AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
+            final long parentNodeId = current.getParentNodeId();
+            final int parentAccessibilityViewId =
+                AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
+            final int parentVirtualDescendantId =
+                AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
+            if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+                    || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
+                AccessibilityNodeInfo parent =
+                    provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
+                if (parent != null) {
+                    SparseLongArray childNodeIds = parent.getChildNodeIds();
+                    final int childCount = childNodeIds.size();
+                    for (int i = 0; i < childCount; i++) {
+                        if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                            return;
+                        }
+                        final long childNodeId = childNodeIds.get(i);
+                        if (childNodeId != current.getSourceNodeId()) {
+                            final int childVirtualDescendantId =
+                                AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
+                            AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
+                                    childVirtualDescendantId);
+                            if (child != null) {
+                                outInfos.add(child);
+                            }
+                        }
+                    }
+                }
+            } else {
+                prefetchSiblingsOfRealNode(providerHost, outInfos);
+            }
+        }
+
+        private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
+                AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
+            SparseLongArray childNodeIds = root.getChildNodeIds();
+            final int initialOutInfosSize = outInfos.size();
+            final int childCount = childNodeIds.size();
+            for (int i = 0; i < childCount; i++) {
+                if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                    return;
+                }
+                final long childNodeId = childNodeIds.get(i);
+                AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
+                        AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
+                if (child != null) {
+                    outInfos.add(child);
+                }
+            }
+            if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                final int addedChildCount = outInfos.size() - initialOutInfosSize;
+                for (int i = 0; i < addedChildCount; i++) {
+                    AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
+                    prefetchDescendantsOfVirtualNode(child, provider, outInfos);
+                }
+            }
+        }
+    }
+
+    private class PrivateHandler extends Handler {
+        private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
+        private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
+        private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 3;
+        private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 4;
+        private final static int MSG_FIND_FOCUS = 5;
+        private final static int MSG_FOCUS_SEARCH = 6;
+
+        public PrivateHandler() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public String getMessageName(Message message) {
+            final int type = message.what;
+            switch (type) {
+                case MSG_PERFORM_ACCESSIBILITY_ACTION:
+                    return "MSG_PERFORM_ACCESSIBILITY_ACTION";
+                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
+                    return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
+                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID:
+                    return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
+                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
+                    return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
+                case MSG_FIND_FOCUS:
+                    return "MSG_FIND_FOCUS";
+                case MSG_FOCUS_SEARCH:
+                    return "MSG_FOCUS_SEARCH";
+                default:
+                    throw new IllegalArgumentException("Unknown message type: " + type);
+            }
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            final int type = message.what;
+            switch (type) {
+                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
+                    findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
+                } break;
+                case MSG_PERFORM_ACCESSIBILITY_ACTION: {
+                    perfromAccessibilityActionUiThread(message);
+                } break;
+                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: {
+                    findAccessibilityNodeInfoByViewIdUiThread(message);
+                } break;
+                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
+                    findAccessibilityNodeInfosByTextUiThread(message);
+                } break;
+                case MSG_FIND_FOCUS: {
+                    findFocusUiThread(message);
+                } break;
+                case MSG_FOCUS_SEARCH: {
+                    focusSearchUiThread(message);
+                } break;
+                default:
+                    throw new IllegalArgumentException("Unknown message type: " + type);
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 3529b8e..8a01c15 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -17,10 +17,12 @@
 package android.view;
 
 import android.graphics.Rect;
+import android.view.ViewGroup.ChildListForAccessibility;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Stack;
 
 /**
  * The algorithm used for finding the next focusable view in a given direction
@@ -30,7 +32,7 @@
 
     private static ThreadLocal<FocusFinder> tlFocusFinder =
             new ThreadLocal<FocusFinder>() {
-
+                @Override
                 protected FocusFinder initialValue() {
                     return new FocusFinder();
                 }
@@ -48,6 +50,10 @@
     Rect mBestCandidateRect = new Rect();
     SequentialFocusComparator mSequentialFocusComparator = new SequentialFocusComparator();
 
+    private final ArrayList<View> mTempList = new ArrayList<View>();
+
+    private Stack<View> mTempStack;
+
     // enforce thread local access
     private FocusFinder() {}
 
@@ -60,7 +66,30 @@
      * @return The next focusable view, or null if none exists.
      */
     public final View findNextFocus(ViewGroup root, View focused, int direction) {
+        return findNextFocus(root, focused, mFocusedRect, direction);
+    }
 
+    /**
+     * Find the next view to take focus in root's descendants, searching from
+     * a particular rectangle in root's coordinates.
+     * @param root Contains focusedRect. Cannot be null.
+     * @param focusedRect The starting point of the search.
+     * @param direction Direction to look.
+     * @return The next focusable view, or null if none exists.
+     */
+    public View findNextFocusFromRect(ViewGroup root, Rect focusedRect, int direction) {
+        return findNextFocus(root, null, focusedRect, direction);
+    }
+
+    private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
+        if ((direction & View.FOCUS_ACCESSIBILITY) != View.FOCUS_ACCESSIBILITY) {
+            return findNextInputFocus(root, focused, focusedRect, direction);
+        } else {
+            return findNextAccessibilityFocus(root, focused, direction);
+        }
+    }
+
+    private View findNextInputFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
         if (focused != null) {
             // check for user specified next focus
             View userSetNextFocus = focused.findUserSetNextFocus(root, direction);
@@ -79,90 +108,154 @@
             switch (direction) {
                 case View.FOCUS_RIGHT:
                 case View.FOCUS_DOWN:
-                    setFocusBottomRight(root);
+                    setFocusTopLeft(root);
                     break;
                 case View.FOCUS_FORWARD:
                     if (root.isLayoutRtl()) {
-                        setFocusTopLeft(root);
-                    } else {
                         setFocusBottomRight(root);
+                    } else {
+                        setFocusTopLeft(root);
                     }
                     break;
 
                 case View.FOCUS_LEFT:
                 case View.FOCUS_UP:
-                    setFocusTopLeft(root);
+                    setFocusBottomRight(root);
                     break;
                 case View.FOCUS_BACKWARD:
                     if (root.isLayoutRtl()) {
-                        setFocusBottomRight(root);
-                    } else {
                         setFocusTopLeft(root);
+                    } else {
+                        setFocusBottomRight(root);
                     break;
                 }
             }
         }
-        return findNextFocus(root, focused, mFocusedRect, direction);
+
+        ArrayList<View> focusables = mTempList;
+        focusables.clear();
+        root.addFocusables(focusables, direction);
+        if (focusables.isEmpty()) {
+            // The focus cannot change.
+            return null;
+        }
+
+        try {
+            switch (direction) {
+                case View.FOCUS_FORWARD:
+                case View.FOCUS_BACKWARD:
+                    return findNextInputFocusInRelativeDirection(focusables, root, focused,
+                            focusedRect, direction);
+                case View.FOCUS_UP:
+                case View.FOCUS_DOWN:
+                case View.FOCUS_LEFT:
+                case View.FOCUS_RIGHT:
+                    return findNextInputFocusInAbsoluteDirection(focusables, root, focused,
+                            focusedRect, direction);
+                default:
+                    throw new IllegalArgumentException("Unknown direction: " + direction);
+            }
+        } finally {
+            focusables.clear();
+        }
     }
 
-    private void setFocusTopLeft(ViewGroup root) {
+    /**
+     * Find the next view to take accessibility focus in root's descendants,
+     * starting from the view that currently is accessibility focused.
+     *
+     * @param root The root which also contains the focused view.
+     * @param focused The current accessibility focused view.
+     * @param direction Direction to look.
+     * @return The next focusable view, or null if none exists.
+     */
+    private View findNextAccessibilityFocus(ViewGroup root, View focused, int direction) {
+        switch (direction) {
+            case View.ACCESSIBILITY_FOCUS_IN:
+            case View.ACCESSIBILITY_FOCUS_OUT:
+            case View.ACCESSIBILITY_FOCUS_FORWARD:
+            case View.ACCESSIBILITY_FOCUS_BACKWARD: {
+                return findNextHierarchicalAcessibilityFocus(root, focused, direction);
+            }
+            case View.ACCESSIBILITY_FOCUS_LEFT:
+            case View.ACCESSIBILITY_FOCUS_RIGHT:
+            case View.ACCESSIBILITY_FOCUS_UP:
+            case View.ACCESSIBILITY_FOCUS_DOWN: {
+                return findNextDirectionalAccessibilityFocus(root, focused, direction);
+            }
+            default:
+                throw new IllegalArgumentException("Unknown direction: " + direction);
+        }
+    }
+
+    private View findNextHierarchicalAcessibilityFocus(ViewGroup root, View focused,
+            int direction) {
+        View current = (focused != null) ? focused : root;
+        switch (direction) {
+            case View.ACCESSIBILITY_FOCUS_IN: {
+                return findNextAccessibilityFocusIn(current);
+            }
+            case View.ACCESSIBILITY_FOCUS_OUT: {
+                return findNextAccessibilityFocusOut(current);
+            }
+            case View.ACCESSIBILITY_FOCUS_FORWARD: {
+                return findNextAccessibilityFocusForward(current);
+            }
+            case View.ACCESSIBILITY_FOCUS_BACKWARD: {
+                return findNextAccessibilityFocusBackward(current);
+            }
+        }
+        return null;
+    }
+
+    private View findNextDirectionalAccessibilityFocus(ViewGroup root, View focused,
+            int direction) {
+        ArrayList<View> focusables = mTempList;
+        focusables.clear();
+        root.addFocusables(focusables, direction, View.FOCUSABLES_ACCESSIBILITY);
+        Rect focusedRect = getFocusedRect(root, focused, direction);
+        final int inputFocusDirection = getCorrespondingInputFocusDirection(direction);
+        View next = findNextInputFocusInAbsoluteDirection(focusables, root,
+                focused, focusedRect, inputFocusDirection);
+        focusables.clear();
+        return next;
+    }
+
+    private View findNextInputFocusInRelativeDirection(ArrayList<View> focusables, ViewGroup root,
+            View focused, Rect focusedRect, int direction) {
+        try {
+            // Note: This sort is stable.
+            mSequentialFocusComparator.setRoot(root);
+            Collections.sort(focusables, mSequentialFocusComparator);
+        } finally {
+            mSequentialFocusComparator.recycle();
+        }
+
+        final int count = focusables.size();
+        switch (direction) {
+            case View.FOCUS_FORWARD:
+                return getForwardFocusable(root, focused, focusables, count);
+            case View.FOCUS_BACKWARD:
+                return getBackwardFocusable(root, focused, focusables, count);
+        }
+        return focusables.get(count - 1);
+    }
+
+    private void setFocusBottomRight(ViewGroup root) {
         final int rootBottom = root.getScrollY() + root.getHeight();
         final int rootRight = root.getScrollX() + root.getWidth();
         mFocusedRect.set(rootRight, rootBottom,
                 rootRight, rootBottom);
     }
 
-    private void setFocusBottomRight(ViewGroup root) {
+    private void setFocusTopLeft(ViewGroup root) {
         final int rootTop = root.getScrollY();
         final int rootLeft = root.getScrollX();
         mFocusedRect.set(rootLeft, rootTop, rootLeft, rootTop);
     }
 
-    /**
-     * Find the next view to take focus in root's descendants, searching from
-     * a particular rectangle in root's coordinates.
-     * @param root Contains focusedRect. Cannot be null.
-     * @param focusedRect The starting point of the search.
-     * @param direction Direction to look.
-     * @return The next focusable view, or null if none exists.
-     */
-    public View findNextFocusFromRect(ViewGroup root, Rect focusedRect, int direction) {
-        return findNextFocus(root, null, focusedRect, direction);
-    }
-
-    private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
-        ArrayList<View> focusables = root.getFocusables(direction);
-        if (focusables.isEmpty()) {
-            // The focus cannot change.
-            return null;
-        }
-
-        if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
-            if (focused != null && !focusables.contains(focused)) {
-                // Add the currently focused view to the list to have it sorted
-                // along with the other views.
-                focusables.add(focused);
-            }
-
-            try {
-                // Note: This sort is stable.
-                mSequentialFocusComparator.setRoot(root);
-                Collections.sort(focusables, mSequentialFocusComparator);
-            } finally {
-                mSequentialFocusComparator.recycle();
-            }
-
-            final int count = focusables.size();
-            switch (direction) {
-                case View.FOCUS_FORWARD:
-                    return getForwardFocusable(root, focused, focusables, count);
-
-                case View.FOCUS_BACKWARD:
-                    return getBackwardFocusable(root, focused, focusables, count);
-            }
-            return null;
-        }
-
+    View findNextInputFocusInAbsoluteDirection(ArrayList<View> focusables, ViewGroup root, View focused,
+            Rect focusedRect, int direction) {
         // initialize the best candidate to something impossible
         // (so the first plausible view will become the best choice)
         mBestCandidateRect.set(focusedRect);
@@ -201,6 +294,140 @@
         return closest;
     }
 
+    private View findNextAccessibilityFocusIn(View view) {
+        // We have to traverse the full view tree to make sure
+        // we consider views in the order specified by their
+        // parent layout managers since some managers could be
+        // LTR while some could be RTL.
+        if (mTempStack == null) {
+            mTempStack = new Stack<View>();
+        }
+        Stack<View> fringe = mTempStack;
+        fringe.clear();
+        fringe.add(view);
+        while (!fringe.isEmpty()) {
+            View current = fringe.pop();
+            if (current.getAccessibilityNodeProvider() != null) {
+                fringe.clear();
+                return current;
+            }
+            if (current != view && current.includeForAccessibility()) {
+                fringe.clear();
+                return current;
+            }
+            if (current instanceof ViewGroup) {
+                ViewGroup currentGroup = (ViewGroup) current;
+                ChildListForAccessibility children = ChildListForAccessibility.obtain(
+                        currentGroup, true);
+                final int childCount = children.getChildCount();
+                for (int i = childCount - 1; i >= 0; i--) {
+                    fringe.push(children.getChildAt(i));
+                }
+                children.recycle();
+            }
+        }
+        return null;
+    }
+
+    private View findNextAccessibilityFocusOut(View view) {
+        ViewParent parent = view.getParentForAccessibility();
+        if (parent instanceof View) {
+            return (View) parent;
+        }
+        return null;
+    }
+
+    private View findNextAccessibilityFocusForward(View view) {
+        // We have to traverse the full view tree to make sure
+        // we consider views in the order specified by their
+        // parent layout managers since some managers could be
+        // LTR while some could be RTL.
+        View current = view;
+        while (current != null) {
+            ViewParent parent = current.getParent();
+            if (!(parent instanceof ViewGroup)) {
+                return null;
+            }
+            ViewGroup parentGroup = (ViewGroup) parent;
+            // Ask the parent to find a sibling after the current view
+            // that can take accessibility focus.
+            ChildListForAccessibility children = ChildListForAccessibility.obtain(
+                    parentGroup, true);
+            final int fromIndex = children.getChildIndex(current) + 1;
+            final int childCount = children.getChildCount();
+            for (int i = fromIndex; i < childCount; i++) {
+                View child = children.getChildAt(i);
+                View next = null;
+                if (child.getAccessibilityNodeProvider() != null) {
+                    next = child;
+                } else if (child.includeForAccessibility()) {
+                    next = child;
+                } else {
+                    next = findNextAccessibilityFocusIn(child);
+                }
+                if (next != null) {
+                    children.recycle();
+                    return next;
+                }
+            }
+            children.recycle();
+            // Reaching a regarded for accessibility predecessor without
+            // finding a next view to take focus means that at this level
+            // there is no next accessibility focusable sibling.
+            if (parentGroup.includeForAccessibility()) {
+                return null;
+            }
+            // Try asking a predecessor to find a focusable.
+            current = parentGroup;
+        }
+        return null;
+    }
+
+    private View findNextAccessibilityFocusBackward(View view) {
+        // We have to traverse the full view tree to make sure
+        // we consider views in the order specified by their
+        // parent layout managers since some managers could be
+        // LTR while some could be RTL.
+        View current = view;
+        while (current != null) {
+            ViewParent parent = current.getParent();
+            if (!(parent instanceof ViewGroup)) {
+                return null;
+            }
+            ViewGroup parentGroup = (ViewGroup) parent;
+            // Ask the parent to find a sibling after the current view
+            // to take accessibility focus
+            ChildListForAccessibility children = ChildListForAccessibility.obtain(
+                    parentGroup, true);
+            final int fromIndex = children.getChildIndex(current) - 1;
+            for (int i = fromIndex; i >= 0; i--) {
+                View child = children.getChildAt(i);
+                View next = null;
+                if (child.getAccessibilityNodeProvider() != null) {
+                    next = child;
+                } else if (child.includeForAccessibility()) {
+                    next = child;
+                } else {
+                    next = findNextAccessibilityFocusIn(child);
+                }
+                if (next != null) {
+                    children.recycle();
+                    return next;
+                }
+            }
+            children.recycle();
+            // Reaching a regarded for accessibility predecessor without
+            // finding a previous view to take focus means that at this level
+            // there is no previous accessibility focusable sibling.
+            if (parentGroup.includeForAccessibility()) {
+                return null;
+            }
+            // Try asking a predecessor to find a focusable.
+            current = parentGroup;
+        }
+        return null;
+    }
+
     private static View getForwardFocusable(ViewGroup root, View focused,
                                             ArrayList<View> focusables, int count) {
         return (root.isLayoutRtl()) ?
@@ -235,6 +462,47 @@
         return focusables.get(count - 1);
     }
 
+    private Rect getFocusedRect(ViewGroup root, View focused, int direction) {
+        Rect focusedRect = mFocusedRect;
+        if (focused != null) {
+            focused.getFocusedRect(focusedRect);
+            root.offsetDescendantRectToMyCoords(focused, focusedRect);
+        } else {
+            switch (direction) {
+                case View.FOCUS_RIGHT:
+                case View.FOCUS_DOWN:
+                    final int rootTop = root.getScrollY();
+                    final int rootLeft = root.getScrollX();
+                    focusedRect.set(rootLeft, rootTop, rootLeft, rootTop);
+                    break;
+
+                case View.FOCUS_LEFT:
+                case View.FOCUS_UP:
+                    final int rootBottom = root.getScrollY() + root.getHeight();
+                    final int rootRight = root.getScrollX() + root.getWidth();
+                    focusedRect.set(rootRight, rootBottom, rootRight, rootBottom);
+                    break;
+            }
+        }
+        return focusedRect;
+    }
+
+    private int getCorrespondingInputFocusDirection(int accessFocusDirection) {
+        switch (accessFocusDirection) {
+            case View.ACCESSIBILITY_FOCUS_LEFT:
+                return View.FOCUS_LEFT;
+            case View.ACCESSIBILITY_FOCUS_RIGHT:
+                return View.FOCUS_RIGHT;
+            case View.ACCESSIBILITY_FOCUS_UP:
+                return View.FOCUS_UP;
+            case View.ACCESSIBILITY_FOCUS_DOWN:
+                return View.FOCUS_DOWN;
+            default:
+                throw new IllegalArgumentException("Cannot map accessiblity focus"
+                        + " direction: " + accessFocusDirection);
+        }
+    }
+
     /**
      * Is rect1 a better candidate than rect2 for a focus search in a particular
      * direction from a source rect?  This is the core routine that determines
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 4ebb679..4848a7a 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,9 +16,12 @@
 
 package android.view;
 
+import android.content.Context;
 import android.hardware.input.InputManager;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Vibrator;
+import android.os.NullVibrator;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -46,8 +49,11 @@
     private final int mSources;
     private final int mKeyboardType;
     private final KeyCharacterMap mKeyCharacterMap;
+    private final boolean mHasVibrator;
     private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
 
+    private Vibrator mVibrator; // guarded by mMotionRanges during initialization
+
     /**
      * A mask for input source classes.
      * 
@@ -304,7 +310,7 @@
 
     // Called by native code.
     private InputDevice(int id, int generation, String name, String descriptor, int sources,
-            int keyboardType, KeyCharacterMap keyCharacterMap) {
+            int keyboardType, KeyCharacterMap keyCharacterMap, boolean hasVibrator) {
         mId = id;
         mGeneration = generation;
         mName = name;
@@ -312,6 +318,7 @@
         mSources = sources;
         mKeyboardType = keyboardType;
         mKeyCharacterMap = keyCharacterMap;
+        mHasVibrator = hasVibrator;
     }
 
     private InputDevice(Parcel in) {
@@ -322,6 +329,7 @@
         mSources = in.readInt();
         mKeyboardType = in.readInt();
         mKeyCharacterMap = KeyCharacterMap.CREATOR.createFromParcel(in);
+        mHasVibrator = in.readInt() != 0;
 
         for (;;) {
             int axis = in.readInt();
@@ -522,6 +530,31 @@
     }
 
     /**
+     * Gets the vibrator service associated with the device, if there is one.
+     * Even if the device does not have a vibrator, the result is never null.
+     * Use {@link Vibrator#hasVibrator} to determine whether a vibrator is
+     * present.
+     *
+     * Note that the vibrator associated with the device may be different from
+     * the system vibrator.  To obtain an instance of the system vibrator instead, call
+     * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as argument.
+     *
+     * @return The vibrator service associated with the device, never null.
+     */
+    public Vibrator getVibrator() {
+        synchronized (mMotionRanges) {
+            if (mVibrator == null) {
+                if (mHasVibrator) {
+                    mVibrator = InputManager.getInstance().getInputDeviceVibrator(mId);
+                } else {
+                    mVibrator = NullVibrator.getInstance();
+                }
+            }
+            return mVibrator;
+        }
+    }
+
+    /**
      * Provides information about the range of values for a particular {@link MotionEvent} axis.
      *
      * @see InputDevice#getMotionRange(int)
@@ -617,6 +650,7 @@
         out.writeInt(mSources);
         out.writeInt(mKeyboardType);
         mKeyCharacterMap.writeToParcel(out, flags);
+        out.writeInt(mHasVibrator ? 1 : 0);
 
         final int numRanges = mMotionRanges.size();
         for (int i = 0; i < numRanges; i++) {
@@ -657,6 +691,8 @@
         }
         description.append("\n");
 
+        description.append("  Has Vibrator: ").append(mHasVibrator).append("\n");
+
         description.append("  Sources: 0x").append(Integer.toHexString(mSources)).append(" (");
         appendSourceDescriptionIfApplicable(description, SOURCE_KEYBOARD, "keyboard");
         appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1fa19d1..962e13a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -975,6 +975,14 @@
     public static final int FOCUSABLES_TOUCH_MODE = 0x00000001;
 
     /**
+     * View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
+     * should add only accessibility focusable Views.
+     *
+     * @hide
+     */
+    public static final int FOCUSABLES_ACCESSIBILITY = 0x00000002;
+
+    /**
      * Use with {@link #focusSearch(int)}. Move focus to the previous selectable
      * item.
      */
@@ -1006,6 +1014,54 @@
      */
     public static final int FOCUS_DOWN = 0x00000082;
 
+    // Accessibility focus directions.
+
+    /**
+     * The accessibility focus which is the current user position when
+     * interacting with the accessibility framework.
+     */
+    public static final int FOCUS_ACCESSIBILITY =  0x00001000;
+
+    /**
+     * Use with {@link #focusSearch(int)}. Move acessibility focus left.
+     */
+    public static final int ACCESSIBILITY_FOCUS_LEFT = FOCUS_LEFT | FOCUS_ACCESSIBILITY;
+
+    /**
+     * Use with {@link #focusSearch(int)}. Move acessibility focus up.
+     */
+    public static final int ACCESSIBILITY_FOCUS_UP = FOCUS_UP | FOCUS_ACCESSIBILITY;
+
+    /**
+     * Use with {@link #focusSearch(int)}. Move acessibility focus right.
+     */
+    public static final int ACCESSIBILITY_FOCUS_RIGHT = FOCUS_RIGHT | FOCUS_ACCESSIBILITY;
+
+    /**
+     * Use with {@link #focusSearch(int)}. Move acessibility focus down.
+     */
+    public static final int ACCESSIBILITY_FOCUS_DOWN = FOCUS_DOWN | FOCUS_ACCESSIBILITY;
+
+    /**
+     * Use with {@link #focusSearch(int)}. Move acessibility focus to the next view.
+     */
+    public static final int ACCESSIBILITY_FOCUS_FORWARD = FOCUS_FORWARD | FOCUS_ACCESSIBILITY;
+
+    /**
+     * Use with {@link #focusSearch(int)}. Move acessibility focus to the previous view.
+     */
+    public static final int ACCESSIBILITY_FOCUS_BACKWARD = FOCUS_BACKWARD | FOCUS_ACCESSIBILITY;
+
+    /**
+     * Use with {@link #focusSearch(int)}. Move acessibility focus in a view.
+     */
+    public static final int ACCESSIBILITY_FOCUS_IN = 0x00000004 | FOCUS_ACCESSIBILITY;
+
+    /**
+     * Use with {@link #focusSearch(int)}. Move acessibility focus out of a view.
+     */
+    public static final int ACCESSIBILITY_FOCUS_OUT = 0x00000008 | FOCUS_ACCESSIBILITY;
+
     /**
      * Bits of {@link #getMeasuredWidthAndState()} and
      * {@link #getMeasuredWidthAndState()} that provide the actual measured size.
@@ -1330,7 +1386,7 @@
         R.attr.state_accelerated,       VIEW_STATE_ACCELERATED,
         R.attr.state_hovered,           VIEW_STATE_HOVERED,
         R.attr.state_drag_can_accept,   VIEW_STATE_DRAG_CAN_ACCEPT,
-        R.attr.state_drag_hovered,      VIEW_STATE_DRAG_HOVERED,
+        R.attr.state_drag_hovered,      VIEW_STATE_DRAG_HOVERED
     };
 
     static {
@@ -1452,7 +1508,8 @@
             | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
             | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
             | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
-            | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED;
+            | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
+            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
 
     /**
      * Temporary Rect currently for use in setBackground().  This will probably
@@ -1784,7 +1841,6 @@
      */
     private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
 
-
     /**
      * Indicates that the view is tracking some sort of transient state
      * that the app should not need to be aware of, but that the framework
@@ -1992,6 +2048,50 @@
     public static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT =
             TEXT_ALIGNMENT_GRAVITY << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
 
+    // Accessiblity constants for mPrivateFlags2
+
+    /**
+     * Shift for accessibility related bits in {@link #mPrivateFlags2}.
+     */
+    static final int IMPORTANT_FOR_ACCESSIBILITY_SHIFT = 20;
+
+    /**
+     * Automatically determine whether a view is important for accessibility.
+     */
+    public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0x00000000;
+
+    /**
+     * The view is important for accessibility.
+     */
+    public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 0x00000001;
+
+    /**
+     * The view is not important for accessibility.
+     */
+    public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002;
+
+    /**
+     * The default whether the view is important for accessiblity.
+     */
+    static final int IMPORTANT_FOR_ACCESSIBILITY_DEFAULT = IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+
+    /**
+     * Mask for obtainig the bits which specify how to determine
+     * whether a view is important for accessibility.
+     */
+    static final int IMPORTANT_FOR_ACCESSIBILITY_MASK = (IMPORTANT_FOR_ACCESSIBILITY_AUTO
+        | IMPORTANT_FOR_ACCESSIBILITY_YES | IMPORTANT_FOR_ACCESSIBILITY_NO)
+        << IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+
+    /**
+     * Flag indicating whether a view has accessibility focus.
+     */
+    static final int ACCESSIBILITY_FOCUSED = 0x00000040 << IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+
+    /**
+     * Flag indicating whether a view state for accessibility has changed.
+     */
+    static final int ACCESSIBILITY_STATE_CHANGED = 0x00000080 << IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
 
     /* End of masks for mPrivateFlags2 */
 
@@ -2952,7 +3052,8 @@
         // Set layout and text direction defaults
         mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << LAYOUT_DIRECTION_MASK_SHIFT) |
                 (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT) |
-                (TEXT_ALIGNMENT_DEFAULT << TEXT_ALIGNMENT_MASK_SHIFT);
+                (TEXT_ALIGNMENT_DEFAULT << TEXT_ALIGNMENT_MASK_SHIFT) |
+                (IMPORTANT_FOR_ACCESSIBILITY_DEFAULT << IMPORTANT_FOR_ACCESSIBILITY_SHIFT);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
         mUserPaddingStart = -1;
@@ -3340,6 +3441,9 @@
                     final int textAlignment = a.getInt(attr, TEXT_ALIGNMENT_DEFAULT);
                     mPrivateFlags2 |= TEXT_ALIGNMENT_FLAGS[textAlignment];
                     break;
+                case R.styleable.View_importantForAccessibility:
+                    setImportantForAccessibility(a.getInt(attr,
+                            IMPORTANT_FOR_ACCESSIBILITY_DEFAULT));
             }
         }
 
@@ -3970,6 +4074,10 @@
 
             onFocusChanged(true, direction, previouslyFocusedRect);
             refreshDrawableState();
+
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                notifyAccessibilityStateChanged();
+            }
         }
     }
 
@@ -4050,16 +4158,21 @@
             }
 
             onFocusChanged(false, 0, null);
+
             refreshDrawableState();
 
             ensureInputFocusOnFirstFocusable();
+
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                notifyAccessibilityStateChanged();
+            }
         }
     }
 
     void ensureInputFocusOnFirstFocusable() {
         View root = getRootView();
         if (root != null) {
-            root.requestFocus(FOCUS_FORWARD);
+            root.requestFocus();
         }
     }
 
@@ -4077,6 +4190,10 @@
 
             onFocusChanged(false, 0, null);
             refreshDrawableState();
+
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                notifyAccessibilityStateChanged();
+            }
         }
     }
 
@@ -4127,7 +4244,10 @@
      */
     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
         if (gainFocus) {
-            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+                requestAccessibilityFocus();
+            }
         }
 
         InputMethodManager imm = InputMethodManager.peekInstance();
@@ -4237,7 +4357,7 @@
      */
     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
         if (mAccessibilityDelegate != null) {
-           mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event);
+            mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event);
         } else {
             sendAccessibilityEventUncheckedInternal(event);
         }
@@ -4257,6 +4377,31 @@
         if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) {
             dispatchPopulateAccessibilityEvent(event);
         }
+        // Intercept accessibility focus events fired by virtual nodes to keep
+        // track of accessibility focus position in such nodes.
+        final int eventType = event.getEventType();
+        switch (eventType) {
+            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
+                final long virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
+                        event.getSourceNodeId());
+                if (virtualNodeId != AccessibilityNodeInfo.UNDEFINED) {
+                    ViewRootImpl viewRootImpl = getViewRootImpl();
+                    if (viewRootImpl != null) {
+                        viewRootImpl.setAccessibilityFocusedHost(this);
+                    }
+                }
+            } break;
+            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
+                final long virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
+                        event.getSourceNodeId());
+                if (virtualNodeId != AccessibilityNodeInfo.UNDEFINED) {
+                    ViewRootImpl viewRootImpl = getViewRootImpl();
+                    if (viewRootImpl != null) {
+                        viewRootImpl.setAccessibilityFocusedHost(null);
+                    }
+                }
+            } break;
+        }
         // In the beginning we called #isShown(), so we know that getParent() is not null.
         getParent().requestSendAccessibilityEvent(this, event);
     }
@@ -4399,7 +4544,7 @@
         event.setContentDescription(mContentDescription);
 
         if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && mAttachInfo != null) {
-            ArrayList<View> focusablesTempList = mAttachInfo.mFocusablesTempList;
+            ArrayList<View> focusablesTempList = mAttachInfo.mTempArrayList;
             getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD,
                     FOCUSABLES_ALL);
             event.setItemCount(focusablesTempList.size());
@@ -4488,10 +4633,9 @@
         info.setBoundsInScreen(bounds);
 
         if ((mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
-            ViewParent parent = getParent();
+            ViewParent parent = getParentForAccessibility();
             if (parent instanceof View) {
-                View parentView = (View) parent;
-                info.setParent(parentView);
+                info.setParent((View) parent);
             }
         }
 
@@ -4503,6 +4647,7 @@
         info.setClickable(isClickable());
         info.setFocusable(isFocusable());
         info.setFocused(isFocused());
+        info.setAccessibilityFocused(isAccessibilityFocused());
         info.setSelected(isSelected());
         info.setLongClickable(isLongClickable());
 
@@ -4597,10 +4742,11 @@
      * true for views that do not have textual representation (For example,
      * ImageButton).
      *
-     * @return The content descriptiopn.
+     * @return The content description.
      *
      * @attr ref android.R.styleable#View_contentDescription
      */
+    @ViewDebug.ExportedProperty(category = "accessibility")
     public CharSequence getContentDescription() {
         return mContentDescription;
     }
@@ -5650,8 +5796,9 @@
      * Adds any focusable views that are descendants of this view (possibly
      * including this view if it is focusable itself) to views. This method
      * adds all focusable views regardless if we are in touch mode or
-     * only views focusable in touch mode if we are in touch mode depending on
-     * the focusable mode paramater.
+     * only views focusable in touch mode if we are in touch mode or
+     * only views that can take accessibility focus if accessibility is enabeld
+     * depending on the focusable mode paramater.
      *
      * @param views Focusable views found so far or null if all we are interested is
      *        the number of focusables.
@@ -5660,19 +5807,32 @@
      *
      * @see #FOCUSABLES_ALL
      * @see #FOCUSABLES_TOUCH_MODE
+     * @see #FOCUSABLES_ACCESSIBILITY
      */
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
-        if (!isFocusable()) {
+        if (views == null) {
             return;
         }
-
-        if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&
-                isInTouchMode() && !isFocusableInTouchMode()) {
-            return;
+        if ((focusableMode & FOCUSABLE_IN_TOUCH_MODE) == FOCUSABLE_IN_TOUCH_MODE) {
+            if (isFocusable() && (!isInTouchMode() || isFocusableInTouchMode())) {
+                views.add(this);
+                return;
+            }
         }
-
-        if (views != null) {
-            views.add(this);
+        if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()
+                    && includeForAccessibility()) {
+                views.add(this);
+                return;
+            }
+        }
+        if ((focusableMode & FOCUSABLES_ALL) == FOCUSABLES_ALL) {
+            if (isFocusable()) {
+                views.add(this);
+                return;
+            }
+        } else {
+            throw new IllegalArgumentException("Unknow focusable mode: " + focusableMode);
         }
     }
 
@@ -5734,6 +5894,149 @@
     }
 
     /**
+     * Returns whether this View is accessibility focused.
+     *
+     * @return True if this View is accessibility focused.
+     */
+    boolean isAccessibilityFocused() {
+        return (mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0;
+    }
+
+    /**
+     * Call this to try to give accessibility focus to this view.
+     *
+     * A view will not actually take focus if {@link AccessibilityManager#isEnabled()}
+     * returns false or the view is no visible or the view already has accessibility
+     * focus.
+     *
+     * See also {@link #focusSearch(int)}, which is what you call to say that you
+     * have focus, and you want your parent to look for the next one.
+     *
+     * @return Whether this view actually took accessibility focus.
+     *
+     * @hide
+     */
+    public boolean requestAccessibilityFocus() {
+        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+            return false;
+        }
+        if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
+            return false;
+        }
+        if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) == 0) {
+            mPrivateFlags2 |= ACCESSIBILITY_FOCUSED;
+            ViewRootImpl viewRootImpl = getViewRootImpl();
+            if (viewRootImpl != null) {
+                viewRootImpl.setAccessibilityFocusedHost(this);
+            }
+            invalidate();
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+            notifyAccessibilityStateChanged();
+            // Try to give input focus to this view - not a descendant.
+            requestFocusNoSearch(View.FOCUS_DOWN, null);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Call this to try to clear accessibility focus of this view.
+     *
+     * See also {@link #focusSearch(int)}, which is what you call to say that you
+     * have focus, and you want your parent to look for the next one.
+     *
+     * @hide
+     */
+    public void clearAccessibilityFocus() {
+        if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
+            mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
+            ViewRootImpl viewRootImpl = getViewRootImpl();
+            if (viewRootImpl != null) {
+                viewRootImpl.setAccessibilityFocusedHost(null);
+            }
+            invalidate();
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+            notifyAccessibilityStateChanged();
+            // Try to move accessibility focus to the input focus.
+            View rootView = getRootView();
+            if (rootView != null) {
+                View inputFocus = rootView.findFocus();
+                if (inputFocus != null) {
+                    inputFocus.requestAccessibilityFocus();
+                }
+            }
+        }
+    }
+
+    /**
+     * Find the best view to take accessibility focus from a hover.
+     * This function finds the deepest actionable view and if that
+     * fails ask the parent to take accessibility focus from hover.
+     *
+     * @param x The X hovered location in this view coorditantes.
+     * @param y The Y hovered location in this view coorditantes.
+     * @return Whether the request was handled.
+     *
+     * @hide
+     */
+    public boolean requestAccessibilityFocusFromHover(float x, float y) {
+        if (onRequestAccessibilityFocusFromHover(x, y)) {
+            return true;
+        }
+        ViewParent parent = mParent;
+        if (parent instanceof View) {
+            View parentView = (View) parent;
+
+            float[] position = mAttachInfo.mTmpTransformLocation;
+            position[0] = x;
+            position[1] = y;
+
+            // Compensate for the transformation of the current matrix.
+            if (!hasIdentityMatrix()) {
+                getMatrix().mapPoints(position);
+            }
+
+            // Compensate for the parent scroll and the offset
+            // of this view stop from the parent top.
+            position[0] += mLeft - parentView.mScrollX;
+            position[1] += mTop - parentView.mScrollY;
+
+            return parentView.requestAccessibilityFocusFromHover(position[0], position[1]);
+        }
+        return false;
+    }
+
+    /**
+     * Requests to give this View focus from hover.
+     *
+     * @param x The X hovered location in this view coorditantes.
+     * @param y The Y hovered location in this view coorditantes.
+     * @return Whether the request was handled.
+     *
+     * @hide
+     */
+    public boolean onRequestAccessibilityFocusFromHover(float x, float y) {
+        if (includeForAccessibility()
+                && (isActionableForAccessibility() || hasListenersForAccessibility())) {
+            return requestAccessibilityFocus();
+        }
+        return false;
+    }
+
+    /**
+     * Clears accessibility focus without calling any callback methods
+     * normally invoked in {@link #clearAccessibilityFocus()}. This method
+     * is used for clearing accessibility focus when giving this focus to
+     * another view.
+     */
+    void clearAccessibilityFocusNoCallbacks() {
+        if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
+            mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
+            invalidate();
+        }
+    }
+
+    /**
      * Call this to try to give focus to a specific view or to one of its
      * descendants.
      *
@@ -5753,7 +6056,6 @@
         return requestFocus(View.FOCUS_DOWN);
     }
 
-
     /**
      * Call this to try to give focus to a specific view or to one of its
      * descendants and give it a hint about what direction focus is heading.
@@ -5805,6 +6107,10 @@
      * @return Whether this view or one of its descendants actually took focus.
      */
     public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+        return requestFocusNoSearch(direction, previouslyFocusedRect);
+    }
+
+    private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
         // need to be focusable
         if ((mViewFlags & FOCUSABLE_MASK) != FOCUSABLE ||
                 (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
@@ -5864,6 +6170,248 @@
     }
 
     /**
+     * Gets the mode for determining whether this View is important for accessibility
+     * which is if it fires accessibility events and if it is reported to
+     * accessibility services that query the screen.
+     *
+     * @return The mode for determining whether a View is important for accessibility.
+     *
+     * @attr ref android.R.styleable#View_importantForAccessibility
+     *
+     * @see #IMPORTANT_FOR_ACCESSIBILITY_YES
+     * @see #IMPORTANT_FOR_ACCESSIBILITY_NO
+     * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
+     */
+    @ViewDebug.ExportedProperty(category = "accessibility", mapping = {
+            @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_AUTO,
+                    to = "IMPORTANT_FOR_ACCESSIBILITY_AUTO"),
+            @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_YES,
+                    to = "IMPORTANT_FOR_ACCESSIBILITY_YES"),
+            @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_NO,
+                    to = "IMPORTANT_FOR_ACCESSIBILITY_NO")
+        })
+    public int getImportantForAccessibility() {
+        return (mPrivateFlags2 & IMPORTANT_FOR_ACCESSIBILITY_MASK)
+                >> IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+    }
+
+    /**
+     * Sets how to determine whether this view is important for accessibility
+     * which is if it fires accessibility events and if it is reported to
+     * accessibility services that query the screen.
+     *
+     * @param mode How to determine whether this view is important for accessibility.
+     *
+     * @attr ref android.R.styleable#View_importantForAccessibility
+     *
+     * @see #IMPORTANT_FOR_ACCESSIBILITY_YES
+     * @see #IMPORTANT_FOR_ACCESSIBILITY_NO
+     * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
+     */
+    public void setImportantForAccessibility(int mode) {
+        if (mode != getImportantForAccessibility()) {
+            mPrivateFlags2 &= ~IMPORTANT_FOR_ACCESSIBILITY_MASK;
+            mPrivateFlags2 |= (mode << IMPORTANT_FOR_ACCESSIBILITY_SHIFT)
+                    & IMPORTANT_FOR_ACCESSIBILITY_MASK;
+            notifyAccessibilityStateChanged();
+        }
+    }
+
+    /**
+     * Gets whether this view should be exposed for accessibility.
+     *
+     * @return Whether the view is exposed for accessibility.
+     *
+     * @hide
+     */
+    public boolean isImportantForAccessibility() {
+        final int mode = (mPrivateFlags2 & IMPORTANT_FOR_ACCESSIBILITY_MASK)
+                >> IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+        switch (mode) {
+            case IMPORTANT_FOR_ACCESSIBILITY_YES:
+                return true;
+            case IMPORTANT_FOR_ACCESSIBILITY_NO:
+                return false;
+            case IMPORTANT_FOR_ACCESSIBILITY_AUTO:
+                return isActionableForAccessibility() || hasListenersForAccessibility();
+            default:
+                throw new IllegalArgumentException("Unknow important for accessibility mode: "
+                        + mode);
+        }
+    }
+
+    /**
+     * Gets the parent for accessibility purposes. Note that the parent for
+     * accessibility is not necessary the immediate parent. It is the first
+     * predecessor that is important for accessibility.
+     *
+     * @return The parent for accessibility purposes.
+     */
+    public ViewParent getParentForAccessibility() {
+        if (mParent instanceof View) {
+            View parentView = (View) mParent;
+            if (parentView.includeForAccessibility()) {
+                return mParent;
+            } else {
+                return mParent.getParentForAccessibility();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Adds the children of a given View for accessibility. Since some Views are
+     * not important for accessibility the children for accessibility are not
+     * necessarily direct children of the riew, rather they are the first level of
+     * descendants important for accessibility.
+     *
+     * @param children The list of children for accessibility.
+     */
+    public void addChildrenForAccessibility(ArrayList<View> children) {
+        if (includeForAccessibility()) {
+            children.add(this);
+        }
+    }
+
+    /**
+     * Whether to regard this view for accessibility. A view is regarded for
+     * accessibility if it is important for accessibility or the querying
+     * accessibility service has explicitly requested that view not
+     * important for accessibility are regarded.
+     *
+     * @return Whether to regard the view for accessibility.
+     */
+    boolean includeForAccessibility() {
+        if (mAttachInfo != null) {
+            if (!mAttachInfo.mIncludeNotImportantViews) {
+                return isImportantForAccessibility();
+            } else {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether the View is considered actionable from
+     * accessibility perspective. Such view are important for
+     * accessiiblity.
+     *
+     * @return True if the view is actionable for accessibility.
+     */
+    private boolean isActionableForAccessibility() {
+        return (isClickable() || isLongClickable() || isFocusable());
+    }
+
+    /**
+     * Returns whether the View has registered callbacks wich makes it
+     * important for accessiiblity.
+     *
+     * @return True if the view is actionable for accessibility.
+     */
+    private boolean hasListenersForAccessibility() {
+        ListenerInfo info = getListenerInfo();
+        return mTouchDelegate != null || info.mOnKeyListener != null
+                || info.mOnTouchListener != null || info.mOnGenericMotionListener != null
+                || info.mOnHoverListener != null || info.mOnDragListener != null;
+    }
+
+    /**
+     * Notifies accessibility services that some view's important for
+     * accessibility state has changed. Note that such notifications
+     * are made at most once every
+     * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
+     * to avoid unnecessary load to the system. Also once a view has
+     * made a notifucation this method is a NOP until the notification has
+     * been sent to clients.
+     *
+     * @hide
+     *
+     * TODO: Makse sure this method is called for any view state change
+     *       that is interesting for accessilility purposes.
+     */
+    public void notifyAccessibilityStateChanged() {
+        if ((mPrivateFlags2 & ACCESSIBILITY_STATE_CHANGED) == 0) {
+            mPrivateFlags2 |= ACCESSIBILITY_STATE_CHANGED;
+            if (mParent != null) {
+                mParent.childAccessibilityStateChanged(this);
+            }
+        }
+    }
+
+    /**
+     * Reset the state indicating the this view has requested clients
+     * interested in its accessiblity state to be notified.
+     *
+     * @hide
+     */
+    public void resetAccessibilityStateChanged() {
+        mPrivateFlags2 &= ~ACCESSIBILITY_STATE_CHANGED;
+    }
+
+    /**
+     * Performs the specified accessibility action on the view. For
+     * possible accessibility actions look at {@link AccessibilityNodeInfo}.
+     *
+     * @param action The action to perform.
+     * @return Whether the action was performed.
+     */
+    public boolean performAccessibilityAction(int action) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_CLICK: {
+                final long now = SystemClock.uptimeMillis();
+                // Send down.
+                MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN,
+                        getWidth() / 2, getHeight() / 2, 0);
+                onTouchEvent(event);
+                // Send up.
+                event.setAction(MotionEvent.ACTION_UP);
+                onTouchEvent(event);
+                // Clean up.
+                event.recycle();
+            } break;
+            case AccessibilityNodeInfo.ACTION_FOCUS: {
+                if (!hasFocus()) {
+                    // Get out of touch mode since accessibility
+                    // wants to move focus around.
+                    getViewRootImpl().ensureTouchMode(false);
+                    return requestFocus();
+                }
+            } break;
+            case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
+                if (hasFocus()) {
+                    clearFocus();
+                    return !isFocused();
+                }
+            } break;
+            case AccessibilityNodeInfo.ACTION_SELECT: {
+                if (!isSelected()) {
+                    setSelected(true);
+                    return isSelected();
+                }
+            } break;
+            case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
+                if (isSelected()) {
+                    setSelected(false);
+                    return !isSelected();
+                }
+            } break;
+            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+                if (!isAccessibilityFocused()) {
+                    return requestAccessibilityFocus();
+                }
+            } break;
+            case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+                if (isAccessibilityFocused()) {
+                    clearAccessibilityFocus();
+                    return true;
+                }
+            } break;
+        }
+        return false;
+    }
+
+    /**
      * @hide
      */
     public void dispatchStartTemporaryDetach() {
@@ -6757,21 +7305,27 @@
         // The root view may receive hover (or touch) events that are outside the bounds of
         // the window.  This code ensures that we only send accessibility events for
         // hovers that are actually within the bounds of the root view.
-        final int action = event.getAction();
+        final int action = event.getActionMasked();
         if (!mSendingHoverAccessibilityEvents) {
             if ((action == MotionEvent.ACTION_HOVER_ENTER
                     || action == MotionEvent.ACTION_HOVER_MOVE)
                     && !hasHoveredChild()
                     && pointInView(event.getX(), event.getY())) {
-                mSendingHoverAccessibilityEvents = true;
                 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+                mSendingHoverAccessibilityEvents = true;
+                requestAccessibilityFocusFromHover((int) event.getX(), (int) event.getY());
             }
         } else {
             if (action == MotionEvent.ACTION_HOVER_EXIT
-                    || (action == MotionEvent.ACTION_HOVER_MOVE
+                    || (action == MotionEvent.ACTION_MOVE
                             && !pointInView(event.getX(), event.getY()))) {
                 mSendingHoverAccessibilityEvents = false;
                 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+                // If the window does not have input focus we take away accessibility
+                // focus as soon as the user stop hovering over the view.
+                if (!mAttachInfo.mHasWindowFocus) {
+                    getViewRootImpl().setAccessibilityFocusedHost(null);
+                }
             }
         }
 
@@ -6795,6 +7349,7 @@
             dispatchGenericMotionEventInternal(event);
             return true;
         }
+
         return false;
     }
 
@@ -6806,7 +7361,6 @@
      */
     private boolean isHoverable() {
         final int viewFlags = mViewFlags;
-        //noinspection SimplifiableIfStatement
         if ((viewFlags & ENABLED_MASK) == DISABLED) {
             return false;
         }
@@ -7130,6 +7684,9 @@
                  */
                 if (mParent != null) mParent.focusableViewAvailable(this);
             }
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                notifyAccessibilityStateChanged();
+            }
         }
 
         if ((flags & VISIBILITY_MASK) == VISIBLE) {
@@ -7161,6 +7718,7 @@
 
             if (((mViewFlags & VISIBILITY_MASK) == GONE)) {
                 if (hasFocus()) clearFocus();
+                clearAccessibilityFocus();
                 destroyDrawingCache();
                 if (mParent instanceof View) {
                     // GONE views noop invalidation, so invalidate the parent
@@ -7185,9 +7743,10 @@
             mPrivateFlags |= DRAWN;
 
             if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE) && hasFocus()) {
-                // root view becoming invisible shouldn't clear focus
+                // root view becoming invisible shouldn't clear focus and accessibility focus
                 if (getRootView() != this) {
                     clearFocus();
+                    clearAccessibilityFocus();
                 }
             }
             if (mAttachInfo != null) {
@@ -7241,6 +7800,12 @@
                 mParent.recomputeViewAttributes(this);
             }
         }
+
+        if (AccessibilityManager.getInstance(mContext).isEnabled()
+                && ((changed & FOCUSABLE) != 0 || (changed & CLICKABLE) != 0
+                        || (changed & LONG_CLICKABLE) != 0 || (changed & ENABLED) != 0)) {
+            notifyAccessibilityStateChanged();
+        }
     }
 
     /**
@@ -7319,6 +7884,7 @@
      * @param canvas the canvas on which to draw the view
      */
     protected void dispatchDraw(Canvas canvas) {
+
     }
 
     /**
@@ -10227,6 +10793,7 @@
         resolvePadding();
         resolveTextDirection();
         resolveTextAlignment();
+        clearAccessibilityFocus();
         if (isFocused()) {
             InputMethodManager imm = InputMethodManager.peekInstance();
             imm.focusIn(this);
@@ -10457,6 +11024,8 @@
 
         resetResolvedLayoutDirection();
         resetResolvedTextAlignment();
+        resetAccessibilityStateChanged();
+        clearAccessibilityFocus();
     }
 
     /**
@@ -13287,6 +13856,9 @@
             invalidate(true);
             refreshDrawableState();
             dispatchSetSelected(selected);
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                notifyAccessibilityStateChanged();
+            }
         }
     }
 
@@ -13456,7 +14028,7 @@
             position[1] += view.mTop;
 
             viewParent = view.mParent;
-        }
+         }
 
         if (viewParent instanceof ViewRootImpl) {
             // *cough*
@@ -16291,7 +16863,7 @@
         /**
          * Temporary list for use in collecting focusable descendents of a view.
          */
-        final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24);
+        final ArrayList<View> mTempArrayList = new ArrayList<View>(24);
 
         /**
          * The id of the window for accessibility purposes.
@@ -16299,6 +16871,17 @@
         int mAccessibilityWindowId = View.NO_ID;
 
         /**
+         * Whether to ingore not exposed for accessibility Views when
+         * reporting the view tree to accessibility services.
+         */
+        boolean mIncludeNotImportantViews;
+
+        /**
+         * The drawable for highlighting accessibility focus.
+         */
+        Drawable mAccessibilityFocusDrawable;
+
+        /**
          * Creates a new set of attachment information with the specified
          * events handler and thread.
          *
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 9d06145..9134966 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -197,7 +197,7 @@
      * 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;
+    private static final int TOUCH_EXPLORE_TAP_SLOP = 80;
 
     /**
      * Delay before dispatching a recurring accessibility event in milliseconds.
@@ -238,7 +238,7 @@
     private final int mDoubleTapTouchSlop;
     private final int mPagingTouchSlop;
     private final int mDoubleTapSlop;
-    private final int mScaledTouchExplorationTapSlop;
+    private final int mScaledTouchExploreTapSlop;
     private final int mWindowTouchSlop;
     private final int mMaximumDrawingCacheSize;
     private final int mOverscrollDistance;
@@ -265,7 +265,7 @@
         mDoubleTapTouchSlop = DOUBLE_TAP_TOUCH_SLOP;
         mPagingTouchSlop = PAGING_TOUCH_SLOP;
         mDoubleTapSlop = DOUBLE_TAP_SLOP;
-        mScaledTouchExplorationTapSlop = TOUCH_EXPLORATION_TAP_SLOP;
+        mScaledTouchExploreTapSlop = TOUCH_EXPLORE_TAP_SLOP;
         mWindowTouchSlop = WINDOW_TOUCH_SLOP;
         //noinspection deprecation
         mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
@@ -302,7 +302,7 @@
         mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f);
         mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
         mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f);
-        mScaledTouchExplorationTapSlop = (int) (density * TOUCH_EXPLORATION_TAP_SLOP + 0.5f);
+        mScaledTouchExploreTapSlop = (int) (density * TOUCH_EXPLORE_TAP_SLOP + 0.5f);
         mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
 
         final Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
@@ -559,8 +559,8 @@
      *
      * @hide
      */
-    public int getScaledTouchExplorationTapSlop() {
-        return mScaledTouchExplorationTapSlop;
+    public int getScaledTouchExploreTapSlop() {
+        return mScaledTouchExploreTapSlop;
     }
 
     /**
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8f6badf..cb37a1c 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -141,6 +141,18 @@
     public static final String DEBUG_LATENCY_TAG = "ViewLatency";
 
     /**
+     * Enables detailed logging of accessibility focus operations.
+     * @hide
+     */
+    public static final boolean DEBUG_ACCESSIBILITY_FOCUS = false;
+
+    /**
+     * Tag for logging of accessibility focus operations
+     * @hide
+     */
+    public static final String DEBUG_ACCESSIBILITY_FOCUS_TAG = "AccessibilityFocus";
+
+    /**
      * <p>Enables or disables views consistency check. Even when this property is enabled,
      * view consistency checks happen only if {@link false} is set
      * to true. The value of this property can be configured externally in one of the
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 121b544..7e90e2b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -45,6 +45,7 @@
 import com.android.internal.util.Predicate;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 
 /**
@@ -611,13 +612,13 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
-        ViewParent parent = getParent();
+        ViewParent parent = mParent;
         if (parent == null) {
             return false;
         }
         final boolean propagate = onRequestSendAccessibilityEvent(child, event);
-        //noinspection SimplifiableIfStatement
         if (!propagate) {
             return false;
         }
@@ -1552,6 +1553,33 @@
         return mFirstHoverTarget != null;
     }
 
+    @Override
+    public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
+        View[] children = mChildren;
+        final int childrenCount = mChildrenCount;
+        for (int i = 0; i < childrenCount; i++) {
+            View child = children[i];
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
+                    && (child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
+                if (child.includeForAccessibility()) {
+                    childrenForAccessibility.add(child);
+                } else {
+                    child.addChildrenForAccessibility(childrenForAccessibility);
+                }
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void childAccessibilityStateChanged(View child) {
+        if (mParent != null) {
+            mParent.childAccessibilityStateChanged(child);
+        }
+    }
+
     /**
      * Implement this method to intercept hover events before they are handled
      * by child views.
@@ -2294,33 +2322,43 @@
 
     @Override
     boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
-        boolean handled = super.dispatchPopulateAccessibilityEventInternal(event);
-        if (handled) {
-            return handled;
+        boolean handled = false;
+        if (includeForAccessibility()) {
+            handled = super.dispatchPopulateAccessibilityEventInternal(event);
+            if (handled) {
+                return handled;
+            }
         }
         // Let our children have a shot in populating the event.
-        for (int i = 0, count = getChildCount(); i < count; i++) {
-            View child = getChildAt(i);
+        ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
+        final int childCount = children.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = children.getChildAt(i);
             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
-                handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event);
+                handled = child.dispatchPopulateAccessibilityEvent(event);
                 if (handled) {
+                    children.recycle();
                     return handled;
                 }
             }
         }
+        children.recycle();
         return false;
     }
 
     @Override
     void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ViewGroup.class.getName());
-        for (int i = 0, count = mChildrenCount; i < count; i++) {
-            View child = mChildren[i];
-            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
-                    && (child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
+        if (mAttachInfo != null) {
+            ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList;
+            childrenForAccessibility.clear();
+            addChildrenForAccessibility(childrenForAccessibility);
+            final int childrenForAccessibilityCount = childrenForAccessibility.size();
+            for (int i = 0; i < childrenForAccessibilityCount; i++) {
+                View child = childrenForAccessibility.get(i);
                 info.addChild(child);
             }
+            childrenForAccessibility.clear();
         }
     }
 
@@ -2331,6 +2369,20 @@
     }
 
     /**
+     * @hide
+     */
+    @Override
+    public void resetAccessibilityStateChanged() {
+        super.resetAccessibilityStateChanged();
+        View[] children = mChildren;
+        final int childCount = mChildrenCount;
+        for (int i = 0; i < childCount; i++) {
+            View child = children[i];
+            child.resetAccessibilityStateChanged();
+        }
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -3400,6 +3452,10 @@
             clearChildFocus(view);
             ensureInputFocusOnFirstFocusable();
         }
+
+        if (view.isAccessibilityFocused()) {
+            view.clearAccessibilityFocus();
+        }
     }
 
     /**
@@ -5622,4 +5678,218 @@
             }
         }
     }
+
+    /**
+     * Pooled class that orderes the children of a ViewGroup from start
+     * to end based on how they are laid out and the layout direction.
+     */
+    static class ChildListForAccessibility {
+
+        private static final int MAX_POOL_SIZE = 32;
+
+        private static final Object sPoolLock = new Object();
+
+        private static ChildListForAccessibility sPool;
+
+        private static int sPoolSize;
+
+        private boolean mIsPooled;
+
+        private ChildListForAccessibility mNext;
+
+        private final ArrayList<View> mChildren = new ArrayList<View>();
+
+        private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>();
+
+        public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) {
+            ChildListForAccessibility list = null;
+            synchronized (sPoolLock) {
+                if (sPool != null) {
+                    list = sPool;
+                    sPool = list.mNext;
+                    list.mNext = null;
+                    list.mIsPooled = false;
+                    sPoolSize--;
+                } else {
+                    list = new ChildListForAccessibility();
+                }
+                list.init(parent, sort);
+                return list;
+            }
+        }
+
+        public void recycle() {
+            if (mIsPooled) {
+                throw new IllegalStateException("Instance already recycled.");
+            }
+            clear();
+            if (sPoolSize < MAX_POOL_SIZE) {
+                mNext = sPool;
+                mIsPooled = true;
+                sPool = this;
+                sPoolSize++;
+            }
+        }
+
+        public int getChildCount() {
+            return mChildren.size();
+        }
+
+        public View getChildAt(int index) {
+            return mChildren.get(index);
+        }
+
+        public int getChildIndex(View child) {
+            return mChildren.indexOf(child);
+        }
+
+        private void init(ViewGroup parent, boolean sort) {
+            ArrayList<View> children = mChildren;
+            final int childCount = parent.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                View child = parent.getChildAt(i);
+                children.add(child);
+            }
+            if (sort) {
+                ArrayList<ViewLocationHolder> holders = mHolders;
+                for (int i = 0; i < childCount; i++) {
+                    View child = children.get(i);
+                    ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child);
+                    holders.add(holder);
+                }
+                Collections.sort(holders);
+                for (int i = 0; i < childCount; i++) {
+                    ViewLocationHolder holder = holders.get(i);
+                    children.set(i, holder.mView);
+                    holder.recycle();
+                }
+                holders.clear();
+            }
+        }
+
+        private void clear() {
+            mChildren.clear();
+        }
+    }
+
+    /**
+     * Pooled class that holds a View and its location with respect to
+     * a specified root. This enables sorting of views based on their
+     * coordinates without recomputing the position relative to the root
+     * on every comparison.
+     */
+    static class ViewLocationHolder implements Comparable<ViewLocationHolder> {
+
+        private static final int MAX_POOL_SIZE = 32;
+
+        private static final Object sPoolLock = new Object();
+
+        private static ViewLocationHolder sPool;
+
+        private static int sPoolSize;
+
+        private boolean mIsPooled;
+
+        private ViewLocationHolder mNext;
+
+        private final Rect mLocation = new Rect();
+
+        public View mView;
+
+        private int mLayoutDirection;
+
+        public static ViewLocationHolder obtain(ViewGroup root, View view) {
+            ViewLocationHolder holder = null;
+            synchronized (sPoolLock) {
+                if (sPool != null) {
+                    holder = sPool;
+                    sPool = holder.mNext;
+                    holder.mNext = null;
+                    holder.mIsPooled = false;
+                    sPoolSize--;
+                } else {
+                    holder = new ViewLocationHolder();
+                }
+                holder.init(root, view);
+                return holder;
+            }
+        }
+
+        public void recycle() {
+            if (mIsPooled) {
+                throw new IllegalStateException("Instance already recycled.");
+            }
+            clear();
+            if (sPoolSize < MAX_POOL_SIZE) {
+                mNext = sPool;
+                mIsPooled = true;
+                sPool = this;
+                sPoolSize++;
+            }
+        }
+
+        @Override
+        public int compareTo(ViewLocationHolder another) {
+            // This instance is greater than an invalid argument.
+            if (another == null) {
+                return 1;
+            }
+            if (getClass() != another.getClass()) {
+                return 1;
+            }
+            // First is above second.
+            if (mLocation.bottom - another.mLocation.top <= 0) {
+                return -1;
+            }
+            // First is below second.
+            if (mLocation.top - another.mLocation.bottom >= 0) {
+                return 1;
+            }
+            // LTR
+            if (mLayoutDirection == LAYOUT_DIRECTION_LTR) {
+                final int leftDifference = mLocation.left - another.mLocation.left;
+                // First more to the left than second.
+                if (leftDifference != 0) {
+                    return leftDifference;
+                }
+            } else { // RTL
+                final int rightDifference = mLocation.right - another.mLocation.right;
+                // First more to the right than second.
+                if (rightDifference != 0) {
+                    return -rightDifference;
+                }
+            }
+            // Break tie by top.
+            final int topDiference = mLocation.top - another.mLocation.top;
+            if (topDiference != 0) {
+                return topDiference;
+            }
+            // Break tie by height.
+            final int heightDiference = mLocation.height() - another.mLocation.height();
+            if (heightDiference != 0) {
+                return -heightDiference;
+            }
+            // Break tie by width.
+            final int widthDiference = mLocation.width() - another.mLocation.width();
+            if (widthDiference != 0) {
+                return -widthDiference;
+            }
+            // Return nondeterministically one of them since we do
+            // not want to ignore any views.
+            return 1;
+        }
+
+        private void init(ViewGroup root, View view) {
+            Rect viewLocation = mLocation;
+            view.getDrawingRect(viewLocation);
+            root.offsetDescendantRectToMyCoords(view, viewLocation);
+            mView = view;
+            mLayoutDirection = root.getResolvedLayoutDirection();
+        }
+
+        private void clear() {
+            mView = null;
+            mLocation.set(0, 0, 0, 0);
+        }
+    }
 }
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 75e9151..ddff91d 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -277,4 +277,22 @@
      * View.fitSystemWindows(Rect)} be performed.
      */
     public void requestFitSystemWindows();
+
+    /**
+     * Gets the parent of a given View for accessibility. Since some Views are not
+     * exposed to the accessibility layer the parent for accessibility is not
+     * necessarily the direct parent of the View, rather it is a predecessor.
+     *
+     * @return The parent or <code>null</code> if no such is found.
+     */
+    public ViewParent getParentForAccessibility();
+
+    /**
+     * A child notifies its parent that its state for accessibility has changed.
+     * That is some of the child properties reported to accessibility services has
+     * changed, hence the interested services have to be notified for the new state.
+     *
+     * @hide
+     */
+    public void childAccessibilityStateChanged(View child);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7a43cf1..b4554d5 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -37,6 +37,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.os.Binder;
 import android.os.Bundle;
@@ -56,17 +57,11 @@
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
-import android.util.Pool;
-import android.util.Poolable;
-import android.util.PoolableManager;
-import android.util.Pools;
 import android.util.Slog;
-import android.util.SparseLongArray;
 import android.util.TypedValue;
 import android.view.View.AttachInfo;
 import android.view.View.MeasureSpec;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -79,6 +74,7 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
 
+import com.android.internal.R;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.view.BaseSurfaceHolder;
 import com.android.internal.view.IInputMethodCallback;
@@ -89,9 +85,7 @@
 import java.io.OutputStream;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.HashSet;
 
 /**
  * The top of a view hierarchy, implementing the needed protocol between View
@@ -181,6 +175,10 @@
     View mFocusedView;
     View mRealFocusedView;  // this is not set to null in touch mode
     View mOldFocusedView;
+
+    View mAccessibilityFocusedHost;
+    AccessibilityNodeInfo mAccessibilityFocusedVirtualView;
+
     int mViewVisibility;
     boolean mAppVisible = true;
     int mOrigWindowType = -1;
@@ -321,7 +319,7 @@
 
     SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent;
 
-    AccessibilityNodePrefetcher mAccessibilityNodePrefetcher;
+    HashSet<View> mTempHashSet;
 
     private final int mDensity;
 
@@ -630,6 +628,10 @@
                 if (mAccessibilityManager.isEnabled()) {
                     mAccessibilityInteractionConnectionManager.ensureConnection();
                 }
+
+                if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+                    view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+                }
             }
         }
     }
@@ -1418,6 +1420,8 @@
 
                             mView.draw(layerCanvas);
 
+                            drawAccessibilityFocusedDrawableIfNeeded(layerCanvas);
+
                             mResizeBufferStartTime = SystemClock.uptimeMillis();
                             mResizeBufferDuration = mView.getResources().getInteger(
                                     com.android.internal.R.integer.config_mediumAnimTime);
@@ -1712,7 +1716,7 @@
             attachInfo.mTreeObserver.dispatchOnGlobalLayout();
 
             if (AccessibilityManager.getInstance(host.mContext).isEnabled()) {
-                postSendWindowContentChangedCallback();
+                postSendWindowContentChangedCallback(mView);
             }
         }
 
@@ -1880,6 +1884,7 @@
             mResizePaint.setAlpha(mResizeAlpha);
             canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint);
         }
+        drawAccessibilityFocusedDrawableIfNeeded(canvas);
     }
 
     /**
@@ -2234,6 +2239,8 @@
 
                 mView.draw(canvas);
 
+                drawAccessibilityFocusedDrawableIfNeeded(canvas);
+
                 if (ViewDebug.DEBUG_LATENCY) {
                     long now = System.nanoTime();
                     Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- draw() took "
@@ -2274,6 +2281,64 @@
         return true;
     }
 
+    /**
+     * We want to draw a highlight around the current accessibility focused.
+     * Since adding a style for all possible view is not a viable option we
+     * have this specialized drawing method.
+     *
+     * Note: We are doing this here to be able to draw the highlight for
+     *       virtual views in addition to real ones.
+     *
+     * @param canvas The canvas on which to draw.
+     */
+    private void drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas) {
+        if (!AccessibilityManager.getInstance(mView.mContext).isEnabled()) {
+            return;
+        }
+        if (mAccessibilityFocusedHost == null || mAccessibilityFocusedHost.mAttachInfo == null) {
+            return;
+        }
+        Drawable drawable = getAccessibilityFocusedDrawable();
+        if (drawable == null) {
+            return;
+        }
+        AccessibilityNodeProvider provider =
+            mAccessibilityFocusedHost.getAccessibilityNodeProvider();
+        Rect bounds = mView.mAttachInfo.mTmpInvalRect;
+        if (provider == null) {
+            mAccessibilityFocusedHost.getDrawingRect(bounds);
+            if (mView instanceof ViewGroup) {
+                ViewGroup viewGroup = (ViewGroup) mView;
+                viewGroup.offsetDescendantRectToMyCoords(mAccessibilityFocusedHost, bounds);
+            }
+        } else {
+            if (mAccessibilityFocusedVirtualView == null) {
+                mAccessibilityFocusedVirtualView = provider.findAccessibilitiyFocus(View.NO_ID);
+            }
+            mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds);
+            bounds.offset(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop);
+        }
+        drawable.setBounds(bounds);
+        drawable.draw(canvas);
+    }
+
+    private Drawable getAccessibilityFocusedDrawable() {
+        if (mAttachInfo != null) {
+            // Lazily load the accessibility focus drawable.
+            if (mAttachInfo.mAccessibilityFocusDrawable == null) {
+                TypedValue value = new TypedValue();
+                final boolean resolved = mView.mContext.getTheme().resolveAttribute(
+                        R.attr.accessibilityFocusedDrawable, value, true);
+                if (resolved) {
+                    mAttachInfo.mAccessibilityFocusDrawable =
+                        mView.mContext.getResources().getDrawable(value.resourceId);
+                }
+            }
+            return mAttachInfo.mAccessibilityFocusDrawable;
+        }
+        return null;
+    }
+
     void invalidateDisplayLists() {
         final ArrayList<DisplayList> displayLists = mDisplayLists;
         final int count = displayLists.size();
@@ -2407,6 +2472,14 @@
         return handled;
     }
 
+    void setAccessibilityFocusedHost(View host) {
+        if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView == null) {
+            mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks();
+        }
+        mAccessibilityFocusedHost = host;
+        mAccessibilityFocusedVirtualView = null;
+    }
+
     public void requestChildFocus(View child, View focused) {
         checkThread();
 
@@ -2437,9 +2510,13 @@
         mFocusedView = mRealFocusedView = null;
     }
 
+    @Override
+    public ViewParent getParentForAccessibility() {
+        return null;
+    }
+
     public void focusableViewAvailable(View v) {
         checkThread();
-
         if (mView != null) {
             if (!mView.hasFocus()) {
                 v.requestFocus();
@@ -2547,7 +2624,7 @@
     /**
      * Return true if child is an ancestor of parent, (or equal to the parent).
      */
-    private static boolean isViewDescendantOf(View child, View parent) {
+    static boolean isViewDescendantOf(View child, View parent) {
         if (child == parent) {
             return true;
         }
@@ -2585,13 +2662,9 @@
     private final static int MSG_DISPATCH_DRAG_LOCATION_EVENT = 16;
     private final static int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17;
     private final static int MSG_UPDATE_CONFIGURATION = 18;
-    private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 19;
-    private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 20;
-    private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 21;
-    private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 22;
-    private final static int MSG_PROCESS_INPUT_EVENTS = 23;
-    private final static int MSG_DISPATCH_SCREEN_STATE = 24;
-    private final static int MSG_INVALIDATE_DISPLAY_LIST = 25;
+    private final static int MSG_PROCESS_INPUT_EVENTS = 19;
+    private final static int MSG_DISPATCH_SCREEN_STATE = 20;
+    private final static int MSG_INVALIDATE_DISPLAY_LIST = 21;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -2633,14 +2706,6 @@
                     return "MSG_DISPATCH_SYSTEM_UI_VISIBILITY";
                 case MSG_UPDATE_CONFIGURATION:
                     return "MSG_UPDATE_CONFIGURATION";
-                case MSG_PERFORM_ACCESSIBILITY_ACTION:
-                    return "MSG_PERFORM_ACCESSIBILITY_ACTION";
-                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
-                    return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
-                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID:
-                    return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
-                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
-                    return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
                 case MSG_PROCESS_INPUT_EVENTS:
                     return "MSG_PROCESS_INPUT_EVENTS";
                 case MSG_DISPATCH_SCREEN_STATE:
@@ -2770,8 +2835,28 @@
                         mHasHadWindowFocus = true;
                     }
 
-                    if (hasWindowFocus && mView != null && mAccessibilityManager.isEnabled()) {
-                        mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+                    if (mView != null && mAccessibilityManager.isEnabled()) {
+                        if (hasWindowFocus) {
+                            mView.sendAccessibilityEvent(
+                                    AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+                            // Give accessibility focus to the view that has input
+                            // focus if such, otherwise to the first one.
+                            if (mView instanceof ViewGroup) {
+                                ViewGroup viewGroup = (ViewGroup) mView;
+                                View focused = viewGroup.findFocus();
+                                if (focused != null) {
+                                    focused.requestAccessibilityFocus();
+                                }
+                            }
+                            // There is no accessibility focus, despite our effort
+                            // above, now just give it to the first view.
+                            if (mAccessibilityFocusedHost == null) {
+                                mView.requestAccessibilityFocus();
+                            }
+                        } else {
+                            // Clear accessibility focus when the window loses input focus.
+                            setAccessibilityFocusedHost(null);
+                        }
                     }
                 }
             } break;
@@ -2828,30 +2913,6 @@
                 }
                 updateConfiguration(config, false);
             } break;
-            case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
-                if (mView != null) {
-                    getAccessibilityInteractionController()
-                        .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg);
-                }
-            } break;
-            case MSG_PERFORM_ACCESSIBILITY_ACTION: {
-                if (mView != null) {
-                    getAccessibilityInteractionController()
-                        .perfromAccessibilityActionUiThread(msg);
-                }
-            } break;
-            case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: {
-                if (mView != null) {
-                    getAccessibilityInteractionController()
-                        .findAccessibilityNodeInfoByViewIdUiThread(msg);
-                }
-            } break;
-            case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
-                if (mView != null) {
-                    getAccessibilityInteractionController()
-                        .findAccessibilityNodeInfosByTextUiThread(msg);
-                }
-            } break;
             case MSG_DISPATCH_SCREEN_STATE: {
                 if (mView != null) {
                     handleScreenStateChange(msg.arg1 == 1);
@@ -2917,28 +2978,25 @@
                 // set yet.
                 final View focused = mView.findFocus();
                 if (focused != null && !focused.isFocusableInTouchMode()) {
-
                     final ViewGroup ancestorToTakeFocus =
                             findAncestorToTakeFocusInTouchMode(focused);
                     if (ancestorToTakeFocus != null) {
                         // there is an ancestor that wants focus after its descendants that
                         // is focusable in touch mode.. give it focus
                         return ancestorToTakeFocus.requestFocus();
-                    } else {
-                        // nothing appropriate to have focus in touch mode, clear it out
-                        mView.unFocus();
-                        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
-                        mFocusedView = null;
-                        mOldFocusedView = null;
-                        return true;
                     }
                 }
+                // nothing appropriate to have focus in touch mode, clear it out
+                mView.unFocus();
+                mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
+                mFocusedView = null;
+                mOldFocusedView = null;
+                return true;
             }
         }
         return false;
     }
 
-
     /**
      * Find an ancestor of focused that wants focus after its descendants and is
      * focusable in touch mode.
@@ -2964,25 +3022,45 @@
 
     private boolean leaveTouchMode() {
         if (mView != null) {
+            boolean inputFocusValid = false;
             if (mView.hasFocus()) {
                 // i learned the hard way to not trust mFocusedView :)
                 mFocusedView = mView.findFocus();
                 if (!(mFocusedView instanceof ViewGroup)) {
                     // some view has focus, let it keep it
-                    return false;
-                } else if (((ViewGroup)mFocusedView).getDescendantFocusability() !=
+                    inputFocusValid = true;
+                } else if (((ViewGroup) mFocusedView).getDescendantFocusability() !=
                         ViewGroup.FOCUS_AFTER_DESCENDANTS) {
                     // some view group has focus, and doesn't prefer its children
                     // over itself for focus, so let them keep it.
-                    return false;
+                    inputFocusValid = true;
                 }
             }
-
-            // find the best view to give focus to in this brave new non-touch-mode
-            // world
-            final View focused = focusSearch(null, View.FOCUS_DOWN);
-            if (focused != null) {
-                return focused.requestFocus(View.FOCUS_DOWN);
+            // In accessibility mode we always have a view that has the
+            // accessibility focus and input focus follows it, i.e. we
+            // try to give input focus to the accessibility focused view.
+            if (!AccessibilityManager.getInstance(mView.mContext).isEnabled()) {
+                // If the current input focus is not valid, find the best view to give
+                // focus to in this brave new non-touch-mode world.
+                if (!inputFocusValid) {
+                    final View focused = focusSearch(null, View.FOCUS_DOWN);
+                    if (focused != null) {
+                        return focused.requestFocus(View.FOCUS_DOWN);
+                    }
+                }
+            } else {
+                // If the current input focus is not valid clear it but do not
+                // give it to another view since the accessibility focus is
+                // leading now and the input one follows.
+                if (!inputFocusValid) {
+                    if (mFocusedView != null) {
+                        mView.unFocus();
+                        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, null);
+                        mFocusedView = null;
+                        mOldFocusedView = null;
+                        return true;
+                    }
+                }
             }
         }
         return false;
@@ -3487,37 +3565,36 @@
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
             int direction = 0;
             switch (event.getKeyCode()) {
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                if (event.hasNoModifiers()) {
-                    direction = View.FOCUS_LEFT;
-                }
-                break;
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                if (event.hasNoModifiers()) {
-                    direction = View.FOCUS_RIGHT;
-                }
-                break;
-            case KeyEvent.KEYCODE_DPAD_UP:
-                if (event.hasNoModifiers()) {
-                    direction = View.FOCUS_UP;
-                }
-                break;
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                if (event.hasNoModifiers()) {
-                    direction = View.FOCUS_DOWN;
-                }
-                break;
-            case KeyEvent.KEYCODE_TAB:
-                if (event.hasNoModifiers()) {
-                    direction = View.FOCUS_FORWARD;
-                } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
-                    direction = View.FOCUS_BACKWARD;
-                }
-                break;
+                case KeyEvent.KEYCODE_DPAD_LEFT:
+                    if (event.hasNoModifiers()) {
+                        direction = View.FOCUS_LEFT;
+                    }
+                    break;
+                case KeyEvent.KEYCODE_DPAD_RIGHT:
+                    if (event.hasNoModifiers()) {
+                        direction = View.FOCUS_RIGHT;
+                    }
+                    break;
+                case KeyEvent.KEYCODE_DPAD_UP:
+                    if (event.hasNoModifiers()) {
+                        direction = View.FOCUS_UP;
+                    }
+                    break;
+                case KeyEvent.KEYCODE_DPAD_DOWN:
+                    if (event.hasNoModifiers()) {
+                        direction = View.FOCUS_DOWN;
+                    }
+                    break;
+                case KeyEvent.KEYCODE_TAB:
+                    if (event.hasNoModifiers()) {
+                        direction = View.FOCUS_FORWARD;
+                    } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
+                        direction = View.FOCUS_BACKWARD;
+                    }
+                    break;
             }
-
             if (direction != 0) {
-                View focused = mView != null ? mView.findFocus() : null;
+                View focused = mView.findFocus();
                 if (focused != null) {
                     View v = focused.focusSearch(direction);
                     if (v != null && v != focused) {
@@ -3532,8 +3609,8 @@
                                     v, mTempRect);
                         }
                         if (v.requestFocus(direction, mTempRect)) {
-                            playSoundEffect(
-                                    SoundEffectConstants.getContantForFocusDirection(direction));
+                            playSoundEffect(SoundEffectConstants
+                                    .getContantForFocusDirection(direction));
                             finishInputEvent(q, true);
                             return;
                         }
@@ -3683,22 +3760,11 @@
                     + " called when there is no mView");
         }
         if (mAccessibilityInteractionController == null) {
-            mAccessibilityInteractionController = new AccessibilityInteractionController();
+            mAccessibilityInteractionController = new AccessibilityInteractionController(this);
         }
         return mAccessibilityInteractionController;
     }
 
-    public AccessibilityNodePrefetcher getAccessibilityNodePrefetcher() {
-        if (mView == null) {
-            throw new IllegalStateException("getAccessibilityNodePrefetcher"
-                    + " called when there is no mView");
-        }
-        if (mAccessibilityNodePrefetcher == null) {
-            mAccessibilityNodePrefetcher = new AccessibilityNodePrefetcher();
-        }
-        return mAccessibilityNodePrefetcher;
-    }
-
     private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
             boolean insetsPending) throws RemoteException {
 
@@ -4375,15 +4441,19 @@
      * This event is send at most once every
      * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
      */
-    private void postSendWindowContentChangedCallback() {
+    private void postSendWindowContentChangedCallback(View source) {
         if (mSendWindowContentChangedAccessibilityEvent == null) {
             mSendWindowContentChangedAccessibilityEvent =
                 new SendWindowContentChangedAccessibilityEvent();
         }
-        if (!mSendWindowContentChangedAccessibilityEvent.mIsPending) {
-            mSendWindowContentChangedAccessibilityEvent.mIsPending = true;
+        View oldSource = mSendWindowContentChangedAccessibilityEvent.mSource;
+        if (oldSource == null) {
+            mSendWindowContentChangedAccessibilityEvent.mSource = source;
             mHandler.postDelayed(mSendWindowContentChangedAccessibilityEvent,
                     ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
+        } else {
+            View newSource = getCommonPredecessor(oldSource, source);
+            mSendWindowContentChangedAccessibilityEvent.mSource = newSource;
         }
     }
 
@@ -4419,6 +4489,46 @@
         return true;
     }
 
+    @Override
+    public void childAccessibilityStateChanged(View child) {
+        postSendWindowContentChangedCallback(child);
+    }
+
+    private View getCommonPredecessor(View first, View second) {
+        if (mAttachInfo != null) {
+            if (mTempHashSet == null) {
+                mTempHashSet = new HashSet<View>();
+            }
+            HashSet<View> seen = mTempHashSet;
+            seen.clear();
+            View firstCurrent = first;
+            while (firstCurrent != null) {
+                seen.add(firstCurrent);
+                ViewParent firstCurrentParent = firstCurrent.mParent;
+                if (firstCurrentParent instanceof View) {
+                    firstCurrent = (View) firstCurrentParent;
+                } else {
+                    firstCurrent = null;
+                }
+            }
+            View secondCurrent = second;
+            while (secondCurrent != null) {
+                if (seen.contains(secondCurrent)) {
+                    seen.clear();
+                    return secondCurrent;
+                }
+                ViewParent secondCurrentParent = secondCurrent.mParent;
+                if (secondCurrentParent instanceof View) {
+                    secondCurrent = (View) secondCurrentParent;
+                } else {
+                    secondCurrent = null;
+                }
+            }
+            seen.clear();
+        }
+        return null;
+    }
+
     void checkThread() {
         if (mThread != Thread.currentThread()) {
             throw new CalledFromWrongThreadException(
@@ -4953,6 +5063,7 @@
                 }
             } else {
                 ensureNoConnection();
+                setAccessibilityFocusedHost(null);
             }
         }
 
@@ -4991,14 +5102,15 @@
             mViewRootImpl = new WeakReference<ViewRootImpl>(viewRootImpl);
         }
 
+        @Override
         public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
-                int prefetchFlags, int interrogatingPid, long interrogatingTid) {
+                int flags, int interrogatingPid, long interrogatingTid) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
                     .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId,
-                        interactionId, callback, prefetchFlags, interrogatingPid, interrogatingTid);
+                        interactionId, callback, flags, interrogatingPid, interrogatingTid);
             } else {
                 // We cannot make the call and notify the caller so it does not wait.
                 try {
@@ -5009,16 +5121,17 @@
             }
         }
 
+        @Override
         public void performAccessibilityAction(long accessibilityNodeId, int action,
                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
-                int interogatingPid, long interrogatingTid) {
+                int flags, int interogatingPid, long interrogatingTid) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
                     .performAccessibilityActionClientThread(accessibilityNodeId, action,
-                            interactionId, callback, interogatingPid, interrogatingTid);
+                            interactionId, callback, flags, interogatingPid, interrogatingTid);
             } else {
-                // We cannot make the call and notify the caller so it does not
+                // We cannot make the call and notify the caller so it does not wait.
                 try {
                     callback.setPerformAccessibilityActionResult(false, interactionId);
                 } catch (RemoteException re) {
@@ -5027,16 +5140,17 @@
             }
         }
 
+        @Override
         public void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId,
                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
-                int interrogatingPid, long interrogatingTid) {
+                int flags, int interrogatingPid, long interrogatingTid) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
                     .findAccessibilityNodeInfoByViewIdClientThread(accessibilityNodeId, viewId,
-                            interactionId, callback, interrogatingPid, interrogatingTid);
+                            interactionId, callback, flags, interrogatingPid, interrogatingTid);
             } else {
-                // We cannot make the call and notify the caller so it does not
+                // We cannot make the call and notify the caller so it does not wait.
                 try {
                     callback.setFindAccessibilityNodeInfoResult(null, interactionId);
                 } catch (RemoteException re) {
@@ -5045,16 +5159,17 @@
             }
         }
 
+        @Override
         public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
-                int interrogatingPid, long interrogatingTid) {
+                int flags, int interrogatingPid, long interrogatingTid) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
                     .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text,
-                            interactionId, callback, interrogatingPid, interrogatingTid);
+                            interactionId, callback, flags, interrogatingPid, interrogatingTid);
             } else {
-                // We cannot make the call and notify the caller so it does not
+                // We cannot make the call and notify the caller so it does not wait.
                 try {
                     callback.setFindAccessibilityNodeInfosResult(null, interactionId);
                 } catch (RemoteException re) {
@@ -5062,610 +5177,54 @@
                 }
             }
         }
-    }
 
-    /**
-     * Computes whether a view is visible on the screen.
-     *
-     * @param view The view to check.
-     * @return Whether the view is visible on the screen.
-     */
-    private boolean isDisplayedOnScreen(View view) {
-        // The first two checks are made also made by isShown() which
-        // however traverses the tree up to the parent to catch that.
-        // Therefore, we do some fail fast check to minimize the up
-        // tree traversal.
-        return (view.mAttachInfo != null
-                && view.mAttachInfo.mWindowVisibility == View.VISIBLE
-                && view.isShown()
-                && view.getGlobalVisibleRect(mTempRect));
-    }
-
-    /**
-     * Class for managing accessibility interactions initiated from the system
-     * and targeting the view hierarchy. A *ClientThread method is to be
-     * called from the interaction connection this ViewAncestor gives the
-     * system to talk to it and a corresponding *UiThread method that is executed
-     * on the UI thread.
-     */
-    final class AccessibilityInteractionController {
-        private static final int POOL_SIZE = 5;
-
-        private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
-            new ArrayList<AccessibilityNodeInfo>();
-
-        // Reusable poolable arguments for interacting with the view hierarchy
-        // to fit more arguments than Message and to avoid sharing objects between
-        // two messages since several threads can send messages concurrently.
-        private final Pool<SomeArgs> mPool = Pools.synchronizedPool(Pools.finitePool(
-                new PoolableManager<SomeArgs>() {
-                    public SomeArgs newInstance() {
-                        return new SomeArgs();
-                    }
-
-                    public void onAcquired(SomeArgs info) {
-                        /* do nothing */
-                    }
-
-                    public void onReleased(SomeArgs info) {
-                        info.clear();
-                    }
-                }, POOL_SIZE)
-        );
-
-        public class SomeArgs implements Poolable<SomeArgs> {
-            private SomeArgs mNext;
-            private boolean mIsPooled;
-
-            public Object arg1;
-            public Object arg2;
-            public int argi1;
-            public int argi2;
-            public int argi3;
-
-            public SomeArgs getNextPoolable() {
-                return mNext;
-            }
-
-            public boolean isPooled() {
-                return mIsPooled;
-            }
-
-            public void setNextPoolable(SomeArgs args) {
-                mNext = args;
-            }
-
-            public void setPooled(boolean isPooled) {
-                mIsPooled = isPooled;
-            }
-
-            private void clear() {
-                arg1 = null;
-                arg2 = null;
-                argi1 = 0;
-                argi2 = 0;
-                argi3 = 0;
-            }
-        }
-
-        public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
-                long accessibilityNodeId, int interactionId,
-                IAccessibilityInteractionConnectionCallback callback, int prefetchFlags,
+        @Override
+        public void findFocus(long accessibilityNodeId, int interactionId, int focusType,
+                IAccessibilityInteractionConnectionCallback callback,  int flags,
                 int interrogatingPid, long interrogatingTid) {
-            Message message = mHandler.obtainMessage();
-            message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
-            message.arg1 = prefetchFlags;
-            SomeArgs args = mPool.acquire();
-            args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
-            args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
-            args.argi3 = interactionId;
-            args.arg1 = callback;
-            message.obj = args;
-            // If the interrogation is performed by the same thread as the main UI
-            // thread in this process, set the message as a static reference so
-            // after this call completes the same thread but in the interrogating
-            // client can handle the message to generate the result.
-            if (interrogatingPid == Process.myPid()
-                    && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
-                AccessibilityInteractionClient.getInstanceForThread(
-                        interrogatingTid).setSameThreadMessage(message);
+            ViewRootImpl viewRootImpl = mViewRootImpl.get();
+            if (viewRootImpl != null && viewRootImpl.mView != null) {
+                viewRootImpl.getAccessibilityInteractionController()
+                    .findFocusClientThread(accessibilityNodeId, interactionId, focusType,
+                            callback, flags, interrogatingPid, interrogatingTid);
             } else {
-                mHandler.sendMessage(message);
-            }
-        }
-
-        public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
-            final int prefetchFlags = message.arg1;
-            SomeArgs args = (SomeArgs) message.obj;
-            final int accessibilityViewId = args.argi1;
-            final int virtualDescendantId = args.argi2;
-            final int interactionId = args.argi3;
-            final IAccessibilityInteractionConnectionCallback callback =
-                (IAccessibilityInteractionConnectionCallback) args.arg1;
-            mPool.release(args);
-            List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
-            infos.clear();
-            try {
-                View target = null;
-                if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) {
-                    target = ViewRootImpl.this.mView;
-                } else {
-                    target = findViewByAccessibilityId(accessibilityViewId);
-                }
-                if (target != null && isDisplayedOnScreen(target)) {
-                    getAccessibilityNodePrefetcher().prefetchAccessibilityNodeInfos(target,
-                            virtualDescendantId, prefetchFlags, infos);
-                }
-            } finally {
+                // We cannot make the call and notify the caller so it does not wait.
                 try {
-                    callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
-                    infos.clear();
+                    callback.setFindAccessibilityNodeInfoResult(null, interactionId);
                 } catch (RemoteException re) {
-                    /* ignore - the other side will time out */
+                    /* best effort - ignore */
                 }
             }
         }
 
-        public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNodeId,
-                int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
+        @Override
+        public void focusSearch(long accessibilityNodeId, int interactionId, int direction,
+                IAccessibilityInteractionConnectionCallback callback, int flags,
                 int interrogatingPid, long interrogatingTid) {
-            Message message = mHandler.obtainMessage();
-            message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
-            message.arg1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
-            SomeArgs args = mPool.acquire();
-            args.argi1 = viewId;
-            args.argi2 = interactionId;
-            args.arg1 = callback;
-            message.obj = args;
-            // If the interrogation is performed by the same thread as the main UI
-            // thread in this process, set the message as a static reference so
-            // after this call completes the same thread but in the interrogating
-            // client can handle the message to generate the result.
-            if (interrogatingPid == Process.myPid()
-                    && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
-                AccessibilityInteractionClient.getInstanceForThread(
-                        interrogatingTid).setSameThreadMessage(message);
+            ViewRootImpl viewRootImpl = mViewRootImpl.get();
+            if (viewRootImpl != null && viewRootImpl.mView != null) {
+                viewRootImpl.getAccessibilityInteractionController()
+                    .focusSearchClientThread(accessibilityNodeId, interactionId, direction,
+                            callback, flags, interrogatingPid, interrogatingTid);
             } else {
-                mHandler.sendMessage(message);
-            }
-        }
-
-        public void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
-            final int accessibilityViewId = message.arg1;
-            SomeArgs args = (SomeArgs) message.obj;
-            final int viewId = args.argi1;
-            final int interactionId = args.argi2;
-            final IAccessibilityInteractionConnectionCallback callback =
-                (IAccessibilityInteractionConnectionCallback) args.arg1;
-            mPool.release(args);
-            AccessibilityNodeInfo info = null;
-            try {
-                View root = null;
-                if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
-                    root = findViewByAccessibilityId(accessibilityViewId);
-                } else {
-                    root = ViewRootImpl.this.mView;
-                }
-                if (root != null) {
-                    View target = root.findViewById(viewId);
-                    if (target != null && isDisplayedOnScreen(target)) {
-                        info = target.createAccessibilityNodeInfo();
-                    }
-                }
-            } finally {
+                // We cannot make the call and notify the caller so it does not wait.
                 try {
-                    callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+                    callback.setFindAccessibilityNodeInfoResult(null, interactionId);
                 } catch (RemoteException re) {
-                    /* ignore - the other side will time out */
+                    /* best effort - ignore */
                 }
             }
         }
-
-        public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
-                String text, int interactionId,
-                IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
-                long interrogatingTid) {
-            Message message = mHandler.obtainMessage();
-            message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT;
-            SomeArgs args = mPool.acquire();
-            args.arg1 = text;
-            args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
-            args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
-            args.argi3 = interactionId;
-            args.arg2 = callback;
-            message.obj = args;
-            // If the interrogation is performed by the same thread as the main UI
-            // thread in this process, set the message as a static reference so
-            // after this call completes the same thread but in the interrogating
-            // client can handle the message to generate the result.
-            if (interrogatingPid == Process.myPid()
-                    && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
-                AccessibilityInteractionClient.getInstanceForThread(
-                        interrogatingTid).setSameThreadMessage(message);
-            } else {
-                mHandler.sendMessage(message);
-            }
-        }
-
-        public void findAccessibilityNodeInfosByTextUiThread(Message message) {
-            SomeArgs args = (SomeArgs) message.obj;
-            final String text = (String) args.arg1;
-            final int accessibilityViewId = args.argi1;
-            final int virtualDescendantId = args.argi2;
-            final int interactionId = args.argi3;
-            final IAccessibilityInteractionConnectionCallback callback =
-                (IAccessibilityInteractionConnectionCallback) args.arg2;
-            mPool.release(args);
-            List<AccessibilityNodeInfo> infos = null;
-            try {
-                View target;
-                if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
-                    target = findViewByAccessibilityId(accessibilityViewId);
-                } else {
-                    target = ViewRootImpl.this.mView;
-                }
-                if (target != null && isDisplayedOnScreen(target)) {
-                    AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
-                    if (provider != null) {
-                        infos = provider.findAccessibilityNodeInfosByText(text,
-                                virtualDescendantId);
-                    } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
-                        ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
-                        foundViews.clear();
-                        target.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
-                                | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
-                                | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
-                        if (!foundViews.isEmpty()) {
-                            infos = mTempAccessibilityNodeInfoList;
-                            infos.clear();
-                            final int viewCount = foundViews.size();
-                            for (int i = 0; i < viewCount; i++) {
-                                View foundView = foundViews.get(i);
-                                if (isDisplayedOnScreen(foundView)) {
-                                    provider = foundView.getAccessibilityNodeProvider();
-                                    if (provider != null) {
-                                        List<AccessibilityNodeInfo> infosFromProvider =
-                                            provider.findAccessibilityNodeInfosByText(text,
-                                                    virtualDescendantId);
-                                        if (infosFromProvider != null) {
-                                            infos.addAll(infosFromProvider);
-                                        }
-                                    } else  {
-                                        infos.add(foundView.createAccessibilityNodeInfo());
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            } finally {
-                try {
-                    callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
-                } catch (RemoteException re) {
-                    /* ignore - the other side will time out */
-                }
-            }
-        }
-
-        public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
-                int interactionId, IAccessibilityInteractionConnectionCallback callback,
-                int interogatingPid, long interrogatingTid) {
-            Message message = mHandler.obtainMessage();
-            message.what = MSG_PERFORM_ACCESSIBILITY_ACTION;
-            message.arg1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
-            message.arg2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
-            SomeArgs args = mPool.acquire();
-            args.argi1 = action;
-            args.argi2 = interactionId;
-            args.arg1 = callback;
-            message.obj = args;
-            // If the interrogation is performed by the same thread as the main UI
-            // thread in this process, set the message as a static reference so
-            // after this call completes the same thread but in the interrogating
-            // client can handle the message to generate the result.
-            if (interogatingPid == Process.myPid()
-                    && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
-                AccessibilityInteractionClient.getInstanceForThread(
-                        interrogatingTid).setSameThreadMessage(message);
-            } else {
-                mHandler.sendMessage(message);
-            }
-        }
-
-        public void perfromAccessibilityActionUiThread(Message message) {
-            final int accessibilityViewId = message.arg1;
-            final int virtualDescendantId = message.arg2;
-            SomeArgs args = (SomeArgs) message.obj;
-            final int action = args.argi1;
-            final int interactionId = args.argi2;
-            final IAccessibilityInteractionConnectionCallback callback =
-                (IAccessibilityInteractionConnectionCallback) args.arg1;
-            mPool.release(args);
-            boolean succeeded = false;
-            try {
-                View target = findViewByAccessibilityId(accessibilityViewId);
-                if (target != null && isDisplayedOnScreen(target)) {
-                    AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
-                    if (provider != null) {
-                        succeeded = provider.performAccessibilityAction(action,
-                                virtualDescendantId);
-                    } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
-                        switch (action) {
-                            case AccessibilityNodeInfo.ACTION_FOCUS: {
-                                if (!target.hasFocus()) {
-                                    // Get out of touch mode since accessibility
-                                    // wants to move focus around.
-                                    ensureTouchMode(false);
-                                    succeeded = target.requestFocus();
-                                }
-                            } break;
-                            case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
-                                if (target.hasFocus()) {
-                                    target.clearFocus();
-                                    succeeded = !target.isFocused();
-                                }
-                            } break;
-                            case AccessibilityNodeInfo.ACTION_SELECT: {
-                                if (!target.isSelected()) {
-                                    target.setSelected(true);
-                                    succeeded = target.isSelected();
-                                }
-                            } break;
-                            case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
-                                if (target.isSelected()) {
-                                    target.setSelected(false);
-                                    succeeded = !target.isSelected();
-                                }
-                            } break;
-                        }
-                    }
-                }
-            } finally {
-                try {
-                    callback.setPerformAccessibilityActionResult(succeeded, interactionId);
-                } catch (RemoteException re) {
-                    /* ignore - the other side will time out */
-                }
-            }
-        }
-
-        private View findViewByAccessibilityId(int accessibilityId) {
-            View root = ViewRootImpl.this.mView;
-            if (root == null) {
-                return null;
-            }
-            View foundView = root.findViewByAccessibilityId(accessibilityId);
-            if (foundView != null && foundView.getVisibility() != View.VISIBLE) {
-                return null;
-            }
-            return foundView;
-        }
     }
 
     private class SendWindowContentChangedAccessibilityEvent implements Runnable {
-        public volatile boolean mIsPending;
+        public View mSource;
 
         public void run() {
-            if (mView != null) {
-                mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-                mIsPending = false;
-            }
-        }
-    }
-
-    /**
-     * This class encapsulates a prefetching strategy for the accessibility APIs for
-     * querying window content. It is responsible to prefetch a batch of
-     * AccessibilityNodeInfos in addition to the one for a requested node.
-     */
-    class AccessibilityNodePrefetcher {
-
-        private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
-
-        public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int prefetchFlags,
-                List<AccessibilityNodeInfo> outInfos) {
-            AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
-            if (provider == null) {
-                AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
-                if (root != null) {
-                    outInfos.add(root);
-                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
-                        prefetchPredecessorsOfRealNode(view, outInfos);
-                    }
-                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
-                        prefetchSiblingsOfRealNode(view, outInfos);
-                    }
-                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
-                        prefetchDescendantsOfRealNode(view, outInfos);
-                    }
-                }
-            } else {
-                AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId);
-                if (root != null) {
-                    outInfos.add(root);
-                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
-                        prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
-                    }
-                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
-                        prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
-                    }
-                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
-                        prefetchDescendantsOfVirtualNode(root, provider, outInfos);
-                    }
-                }
-            }
-        }
-
-        private void prefetchPredecessorsOfRealNode(View view,
-                List<AccessibilityNodeInfo> outInfos) {
-            ViewParent parent = view.getParent();
-            while (parent instanceof View
-                    && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
-                View parentView = (View) parent;
-                final long parentNodeId = AccessibilityNodeInfo.makeNodeId(
-                        parentView.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
-                AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
-                if (info != null) {
-                    outInfos.add(info);
-                }
-                parent = parent.getParent();
-            }
-        }
-
-        private void prefetchSiblingsOfRealNode(View current,
-                List<AccessibilityNodeInfo> outInfos) {
-            ViewParent parent = current.getParent();
-            if (parent instanceof ViewGroup) {
-                ViewGroup parentGroup = (ViewGroup) parent;
-                final int childCount = parentGroup.getChildCount();
-                for (int i = 0; i < childCount; i++) {
-                    View child = parentGroup.getChildAt(i);
-                    if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE
-                            && child.getAccessibilityViewId() != current.getAccessibilityViewId()
-                            && isDisplayedOnScreen(child)) {
-                        final long childNodeId = AccessibilityNodeInfo.makeNodeId(
-                                child.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
-                        AccessibilityNodeInfo info = null;
-                        AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
-                        if (provider == null) {
-                            info = child.createAccessibilityNodeInfo();
-                        } else {
-                            info = provider.createAccessibilityNodeInfo(
-                                    AccessibilityNodeInfo.UNDEFINED);
-                        }
-                        if (info != null) {
-                            outInfos.add(info);
-                        }
-                    }
-                }
-            }
-        }
-
-        private void prefetchDescendantsOfRealNode(View root,
-                List<AccessibilityNodeInfo> outInfos) {
-            if (root instanceof ViewGroup) {
-                ViewGroup rootGroup = (ViewGroup) root;
-                HashMap<View, AccessibilityNodeInfo> addedChildren =
-                    new HashMap<View, AccessibilityNodeInfo>();
-                final int childCount = rootGroup.getChildCount();
-                for (int i = 0; i < childCount; i++) {
-                    View child = rootGroup.getChildAt(i);
-                    if (isDisplayedOnScreen(child)
-                            && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
-                        final long childNodeId = AccessibilityNodeInfo.makeNodeId(
-                                child.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
-                        AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
-                        if (provider == null) {
-                            AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
-                            if (info != null) {
-                                outInfos.add(info);
-                                addedChildren.put(child, null);
-                            }
-                        } else {
-                            AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
-                                   AccessibilityNodeInfo.UNDEFINED);
-                            if (info != null) {
-                                outInfos.add(info);
-                                addedChildren.put(child, info);
-                            }
-                        }
-                    }
-                }
-                if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
-                    for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
-                        View addedChild = entry.getKey();
-                        AccessibilityNodeInfo virtualRoot = entry.getValue();
-                        if (virtualRoot == null) {
-                            prefetchDescendantsOfRealNode(addedChild, outInfos);
-                        } else {
-                            AccessibilityNodeProvider provider =
-                                addedChild.getAccessibilityNodeProvider();
-                            prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
-                        }
-                    }
-                }
-            }
-        }
-
-        private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
-                View providerHost, AccessibilityNodeProvider provider,
-                List<AccessibilityNodeInfo> outInfos) {
-            long parentNodeId = root.getParentNodeId();
-            int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
-            while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
-                final int virtualDescendantId =
-                    AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
-                if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
-                        || accessibilityViewId == providerHost.getAccessibilityViewId()) {
-                    AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
-                            virtualDescendantId);
-                    if (parent != null) {
-                        outInfos.add(parent);
-                    }
-                    parentNodeId = parent.getParentNodeId();
-                    accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
-                            parentNodeId);
-                } else {
-                    prefetchPredecessorsOfRealNode(providerHost, outInfos);
-                    return;
-                }
-            }
-        }
-
-        private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
-                AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
-            final long parentNodeId = current.getParentNodeId();
-            final int parentAccessibilityViewId =
-                AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
-            final int parentVirtualDescendantId =
-                AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
-            if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
-                    || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
-                AccessibilityNodeInfo parent =
-                    provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
-                if (parent != null) {
-                    SparseLongArray childNodeIds = parent.getChildNodeIds();
-                    final int childCount = childNodeIds.size();
-                    for (int i = 0; i < childCount; i++) {
-                        final long childNodeId = childNodeIds.get(i);
-                        if (childNodeId != current.getSourceNodeId()
-                                && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
-                            final int childVirtualDescendantId =
-                                AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
-                            AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
-                                    childVirtualDescendantId);
-                            if (child != null) {
-                                outInfos.add(child);
-                            }
-                        }
-                    }
-                }
-            } else {
-                prefetchSiblingsOfRealNode(providerHost, outInfos);
-            }
-        }
-
-        private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
-                AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
-            SparseLongArray childNodeIds = root.getChildNodeIds();
-            final int initialOutInfosSize = outInfos.size();
-            final int childCount = childNodeIds.size();
-            for (int i = 0; i < childCount; i++) {
-                if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
-                    final long childNodeId = childNodeIds.get(i);
-                    AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
-                            AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
-                    if (child != null) {
-                        outInfos.add(child);
-                    }
-                }
-            }
-            if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
-                final int addedChildCount = outInfos.size() - initialOutInfosSize;
-                for (int i = 0; i < addedChildCount; i++) {
-                    AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
-                    prefetchDescendantsOfVirtualNode(child, provider, outInfos);
-                }
+            if (mSource != null) {
+                mSource.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+                mSource.resetAccessibilityStateChanged();
+                mSource = null;
             }
         }
     }
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index 9152cc3..110c239 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -263,7 +263,7 @@
                 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
 
         mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
-        mVibrator = new Vibrator();
+        mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
 
         mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
         mShowCombinedVolumes = !mVoiceCapable && !useMasterVolume;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 0998c80..6cb1578 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -508,7 +508,7 @@
     public static final int TYPE_VIEW_SELECTED = 0x00000004;
 
     /**
-     * Represents the event of focusing a {@link android.view.View}.
+     * Represents the event of setting input focus of a {@link android.view.View}.
      */
     public static final int TYPE_VIEW_FOCUSED = 0x00000008;
 
@@ -549,7 +549,8 @@
     public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400;
 
     /**
-     * Represents the event of changing the content of a window.
+     * Represents the event of changing the content of a window and more
+     * specifically the sub-tree rooted at the event's source.
      */
     public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800;
 
@@ -569,6 +570,16 @@
     public static final int TYPE_ANNOUNCEMENT = 0x00004000;
 
     /**
+     * Represents the event of gaining accessibility focus.
+     */
+    public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000;
+
+    /**
+     * Represents the event of clearing accessibility focus.
+     */
+    public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000;
+
+    /**
      * Mask for {@link AccessibilityEvent} all types.
      *
      * @see #TYPE_VIEW_CLICKED
@@ -1018,6 +1029,10 @@
                 return "TYPE_VIEW_SCROLLED";
             case TYPE_ANNOUNCEMENT:
                 return "TYPE_ANNOUNCEMENT";
+            case TYPE_VIEW_ACCESSIBILITY_FOCUSED:
+                return "TYPE_VIEW_ACCESSIBILITY_FOCUSED";
+            case TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
+                return "TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED";
             default:
                 return null;
         }
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index be74b31..35f0d9d 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -18,7 +18,9 @@
 
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.graphics.Rect;
+import android.os.Binder;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
@@ -174,7 +176,7 @@
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
                 final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
                         accessibilityWindowId, accessibilityNodeId, interactionId, this,
-                        Thread.currentThread().getId(), prefetchFlags);
+                        prefetchFlags, Thread.currentThread().getId());
                 // If the scale is zero the call has failed.
                 if (windowScale > 0) {
                     List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
@@ -293,6 +295,96 @@
     }
 
     /**
+     * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the
+     * specified focus type. The search is performed in the window whose id is specified
+     * and starts from the node whose accessibility id is specified.
+     *
+     * @param connectionId The id of a connection for interacting with the system.
+     * @param accessibilityWindowId A unique window id. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+     *     to query the currently active window.
+     * @param accessibilityNodeId A unique view id or virtual descendant id from
+     *     where to start the search. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+     *     to start from the root.
+     * @param focusType The focus type.
+     * @return The accessibility focused {@link AccessibilityNodeInfo}.
+     */
+    public AccessibilityNodeInfo findFocus(int connectionId, int accessibilityWindowId,
+            long accessibilityNodeId, int focusType) {
+        try {
+            IAccessibilityServiceConnection connection = getConnection(connectionId);
+            if (connection != null) {
+                final int interactionId = mInteractionIdCounter.getAndIncrement();
+                final float windowScale = connection.findFocus(accessibilityWindowId,
+                        accessibilityNodeId, focusType, interactionId, this,
+                        Thread.currentThread().getId());
+                // If the scale is zero the call has failed.
+                if (windowScale > 0) {
+                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
+                            interactionId);
+                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
+                    return info;
+                }
+            } else {
+                if (DEBUG) {
+                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+                }
+            }
+        } catch (RemoteException re) {
+            if (DEBUG) {
+                Log.w(LOG_TAG, "Error while calling remote findAccessibilityFocus", re);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Finds the accessibility focused {@link android.view.accessibility.AccessibilityNodeInfo}.
+     * The search is performed in the window whose id is specified and starts from the
+     * node whose accessibility id is specified.
+     *
+     * @param connectionId The id of a connection for interacting with the system.
+     * @param accessibilityWindowId A unique window id. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+     *     to query the currently active window.
+     * @param accessibilityNodeId A unique view id or virtual descendant id from
+     *     where to start the search. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+     *     to start from the root.
+     * @param direction The direction in which to search for focusable.
+     * @return The accessibility focused {@link AccessibilityNodeInfo}.
+     */
+    public AccessibilityNodeInfo focusSearch(int connectionId, int accessibilityWindowId,
+            long accessibilityNodeId, int direction) {
+        try {
+            IAccessibilityServiceConnection connection = getConnection(connectionId);
+            if (connection != null) {
+                final int interactionId = mInteractionIdCounter.getAndIncrement();
+                final float windowScale = connection.focusSearch(accessibilityWindowId,
+                        accessibilityNodeId, direction, interactionId, this,
+                        Thread.currentThread().getId());
+                // If the scale is zero the call has failed.
+                if (windowScale > 0) {
+                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
+                            interactionId);
+                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
+                    return info;
+                }
+            } else {
+                if (DEBUG) {
+                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+                }
+            }
+        } catch (RemoteException re) {
+            if (DEBUG) {
+                Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
      *
      * @param connectionId The id of a connection for interacting with the system.
@@ -382,7 +474,12 @@
                 int interactionId) {
         synchronized (mInstanceLock) {
             final boolean success = waitForResultTimedLocked(interactionId);
-            List<AccessibilityNodeInfo> result = success ? mFindAccessibilityNodeInfosResult : null;
+            List<AccessibilityNodeInfo> result = null;
+            if (success) {
+                result = mFindAccessibilityNodeInfosResult;
+            } else {
+                result = Collections.emptyList();
+            }
             clearResultLocked();
             return result;
         }
@@ -395,13 +492,18 @@
                 int interactionId) {
         synchronized (mInstanceLock) {
             if (interactionId > mInteractionId) {
-                // If the call is not an IPC, i.e. it is made from the same process, we need to
-                // instantiate new result list to avoid passing internal instances to clients.
-                final boolean isIpcCall = (queryLocalInterface(getInterfaceDescriptor()) == null);
-                if (!isIpcCall) {
-                    mFindAccessibilityNodeInfosResult = new ArrayList<AccessibilityNodeInfo>(infos);
+                if (infos != null) {
+                    // If the call is not an IPC, i.e. it is made from the same process, we need to
+                    // instantiate new result list to avoid passing internal instances to clients.
+                    final boolean isIpcCall = (Binder.getCallingPid() != Process.myPid());
+                    if (!isIpcCall) {
+                        mFindAccessibilityNodeInfosResult =
+                            new ArrayList<AccessibilityNodeInfo>(infos);
+                    } else {
+                        mFindAccessibilityNodeInfosResult = infos;
+                    }
                 } else {
-                    mFindAccessibilityNodeInfosResult = infos;
+                    mFindAccessibilityNodeInfosResult = Collections.emptyList();
                 }
                 mInteractionId = interactionId;
             }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index e37de6f..77fd12a 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -204,6 +204,12 @@
      * @param event The event to send.
      *
      * @throws IllegalStateException if accessibility is not enabled.
+     *
+     * <strong>Note:</strong> The preferred mechanism for sending custom accessibility
+     * events is through calling
+     * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)}
+     * instead of this method to allow predecessors to augment/filter events sent by
+     * their descendants.
      */
     public void sendAccessibilityEvent(AccessibilityEvent event) {
         if (!mIsEnabled) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index f616dca..1071c65 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -74,29 +74,57 @@
     public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
 
     /** @hide */
-    public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000003;
+    public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004;
+
+    /** @hide */
+    public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008;
 
     // Actions.
 
     /**
-     * Action that focuses the node.
+     * Action that gives input focus to the node.
      */
-    public static final int ACTION_FOCUS =  0x00000001;
+    public static final int ACTION_FOCUS = 0x00000001;
 
     /**
-     * Action that unfocuses the node.
+     * Action that clears input focus of the node.
      */
-    public static final int ACTION_CLEAR_FOCUS =  0x00000002;
+    public static final int ACTION_CLEAR_FOCUS = 0x00000002;
 
     /**
      * Action that selects the node.
      */
-    public static final int ACTION_SELECT =  0x00000004;
+    public static final int ACTION_SELECT = 0x00000004;
 
     /**
      * Action that unselects the node.
      */
-    public static final int ACTION_CLEAR_SELECTION =  0x00000008;
+    public static final int ACTION_CLEAR_SELECTION = 0x00000008;
+
+    /**
+     * Action that gives accessibility focus to the node.
+     */
+    public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000010;
+
+    /**
+     * Action that clears accessibility focus of the node.
+     */
+    public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000020;
+
+    /**
+     * Action that clicks on the node info./AccessibilityNodeInfoCache.java
+     */
+    public static final int ACTION_CLICK = 0x00000040;
+
+    /**
+     * The input focus.
+     */
+    public static final int FOCUS_INPUT = 1;
+
+    /**
+     * The accessibility focus.
+     */
+    public static final int FOCUS_ACCESSIBILITY = 2;
 
     // Boolean attributes.
 
@@ -120,6 +148,8 @@
 
     private static final int PROPERTY_SCROLLABLE = 0x00000200;
 
+    private static final int PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
+
     /**
      * Bits that provide the id of a virtual descendant of a view.
      */
@@ -248,6 +278,57 @@
             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
         mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
     }
+    
+    /**
+     * Find the view that has the input focus. The search starts from
+     * the view represented by this node info.
+     *
+     * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
+     *         {@link #FOCUS_ACCESSIBILITY}.
+     * @return The node info of the focused view or null.
+     *
+     * @see #FOCUS_INPUT
+     * @see #FOCUS_ACCESSIBILITY
+     */
+    public AccessibilityNodeInfo findFocus(int focus) {
+        enforceSealed();
+        if (!canPerformRequestOverConnection(mSourceNodeId)) {
+            return null;
+        }
+        return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId,
+                mSourceNodeId, focus);
+    }
+
+    /**
+     * Searches for the nearest view in the specified direction that can take
+     * the input focus.
+     *
+     * @param direction The direction. Can be one of:
+     *     {@link View#FOCUS_DOWN},
+     *     {@link View#FOCUS_UP},
+     *     {@link View#FOCUS_LEFT},
+     *     {@link View#FOCUS_RIGHT},
+     *     {@link View#FOCUS_FORWARD},
+     *     {@link View#FOCUS_BACKWARD},
+     *     {@link View#ACCESSIBILITY_FOCUS_IN},
+     *     {@link View#ACCESSIBILITY_FOCUS_OUT},
+     *     {@link View#ACCESSIBILITY_FOCUS_FORWARD},
+     *     {@link View#ACCESSIBILITY_FOCUS_BACKWARD},
+     *     {@link View#ACCESSIBILITY_FOCUS_UP},
+     *     {@link View#ACCESSIBILITY_FOCUS_RIGHT},
+     *     {@link View#ACCESSIBILITY_FOCUS_DOWN},
+     *     {@link View#ACCESSIBILITY_FOCUS_LEFT}.
+     *
+     * @return The node info for the view that can take accessibility focus.
+     */
+    public AccessibilityNodeInfo focusSearch(int direction) {
+        enforceSealed();
+        if (!canPerformRequestOverConnection(mSourceNodeId)) {
+            return null;
+        }
+        return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId,
+                mSourceNodeId, direction);
+    }
 
     /**
      * Gets the id of the window from which the info comes from.
@@ -642,6 +723,31 @@
     }
 
     /**
+     * Gets whether this node is accessibility focused.
+     *
+     * @return True if the node is accessibility focused.
+     */
+    public boolean isAccessibilityFocused() {
+        return getBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED);
+    }
+
+    /**
+     * Sets whether this node is accessibility focused.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param focused True if the node is accessibility focused.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setAccessibilityFocused(boolean focused) {
+        setBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED, focused);
+    }
+
+    /**
      * Gets whether this node is selected.
      *
      * @return True if the node is selected.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
index dfbfc70..d2609bb 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
@@ -18,6 +18,7 @@
 
 import android.util.Log;
 import android.util.LongSparseArray;
+import android.util.SparseLongArray;
 
 /**
  * Simple cache for AccessibilityNodeInfos. The cache is mapping an
@@ -54,20 +55,25 @@
      * @param event An event.
      */
     public void onAccessibilityEvent(AccessibilityEvent event) {
-        final int eventType = event.getEventType();
-        switch (eventType) {
-            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
-            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
-            case AccessibilityEvent.TYPE_VIEW_SCROLLED:
-                clear();
-                break;
-            case AccessibilityEvent.TYPE_VIEW_FOCUSED:
-            case AccessibilityEvent.TYPE_VIEW_SELECTED:
-            case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
-            case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:
-                final long accessibilityNodeId = event.getSourceNodeId();
-                remove(accessibilityNodeId);
-                break;
+        if (ENABLED) {
+            final int eventType = event.getEventType();
+            switch (eventType) {
+                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
+                    clear();
+                } break;
+                case AccessibilityEvent.TYPE_VIEW_FOCUSED:
+                case AccessibilityEvent.TYPE_VIEW_SELECTED:
+                case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
+                case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
+                    final long accessibilityNodeId = event.getSourceNodeId();
+                    remove(accessibilityNodeId);
+                } break;
+                case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
+                case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
+                    final long accessibilityNodeId = event.getSourceNodeId();
+                    clearSubTree(accessibilityNodeId);
+                } break;
+            }
         }
     }
 
@@ -167,4 +173,23 @@
             }
         }
     }
+
+    /**
+     * Clears a subtree rooted at the node with the given id.
+     *
+     * @param rootNodeId The root id.
+     */
+    private void clearSubTree(long rootNodeId) {
+        AccessibilityNodeInfo current = mCacheImpl.get(rootNodeId);
+        if (current == null) {
+            return;
+        }
+        mCacheImpl.remove(rootNodeId);
+        SparseLongArray childNodeIds = current.getChildNodeIds();
+        final int childCount = childNodeIds.size();
+        for (int i = 0; i < childCount; i++) {
+            final long childNodeId = childNodeIds.valueAt(i);
+            clearSubTree(childNodeId);
+        }
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
index 5890417..19e35dd 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -87,6 +87,7 @@
      * @return A populated {@link AccessibilityNodeInfo} for a virtual descendant or the
      *     host View.
      *
+     * @see View#createAccessibilityNodeInfo()
      * @see AccessibilityNodeInfo
      */
     public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
@@ -102,6 +103,7 @@
      * @param virtualViewId A client defined virtual view id.
      * @return True if the action was performed.
      *
+     * @see View#performAccessibilityAction(int)
      * @see #createAccessibilityNodeInfo(int)
      * @see AccessibilityNodeInfo
      */
@@ -127,4 +129,58 @@
             int virtualViewId) {
         return null;
     }
+
+    /**
+     * Finds the accessibility focused {@link AccessibilityNodeInfo}. The search is
+     * relative to the virtual view, i.e. a descendant of the host View, with the
+     * given <code>virtualViewId</code> or the host View itself
+     * <code>virtualViewId</code> equals to {@link View#NO_ID}.
+     *
+     * <strong>Note:</strong> Normally the system is responsible to transparently find
+     *     accessibility focused view starting from a given root but for virtual view
+     *     hierarchies it is a responsibility of this provider's implementor to find
+     *     the accessibility focused virtual view.
+     *
+     * @param virtualViewId A client defined virtual view id which defined
+     *     the root of the tree in which to perform the search.
+     * @return A list of node info.
+     *
+     * @see #createAccessibilityNodeInfo(int)
+     * @see AccessibilityNodeInfo
+     */
+    public AccessibilityNodeInfo findAccessibilitiyFocus(int virtualViewId) {
+        return null;
+    }
+
+    /**
+     * Finds {@link AccessibilityNodeInfo} to take accessibility focus in the given
+     * <code>direction</code>. The search is relative to the virtual view, i.e. a
+     * descendant of the host View, with the given <code>virtualViewId</code> or
+     * the host View itself <code>virtualViewId</code> equals to {@link View#NO_ID}.
+     *
+     * <strong>Note:</strong> Normally the system is responsible to transparently find
+     *     the next view to take accessibility focus but for virtual view hierarchies
+     *     it is a responsibility of this provider's implementor to compute the next
+     *     focusable.
+     *
+     * @param direction The direction in which to search for a focus candidate.
+     *     Values are
+     *     {@link View#ACCESSIBILITY_FOCUS_IN},
+     *     {@link View#ACCESSIBILITY_FOCUS_OUT},
+     *     {@link View#ACCESSIBILITY_FOCUS_FORWARD},
+     *     {@link View#ACCESSIBILITY_FOCUS_BACKWARD},
+     *     {@link View#ACCESSIBILITY_FOCUS_UP},
+     *     {@link View#ACCESSIBILITY_FOCUS_DOWN},
+     *     {@link View#ACCESSIBILITY_FOCUS_LEFT},
+     *     {@link View#ACCESSIBILITY_FOCUS_RIGHT}.
+     * @param virtualViewId A client defined virtual view id which defined
+     *     the root of the tree in which to perform the search.
+     * @return A list of node info.
+     *
+     * @see #createAccessibilityNodeInfo(int)
+     * @see AccessibilityNodeInfo
+     */
+    public AccessibilityNodeInfo accessibilityFocusSearch(int direction, int virtualViewId) {
+        return null;
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index d25b3db..78a7d46 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -62,6 +62,7 @@
     private static final int PROPERTY_PASSWORD = 0x00000004;
     private static final int PROPERTY_FULL_SCREEN = 0x00000080;
     private static final int PROPERTY_SCROLLABLE = 0x00000100;
+    private static final int PROPERTY_IMPORTANT_FOR_ACCESSIBILITY = 0x00000200;
 
     private static final int GET_SOURCE_PREFETCH_FLAGS =
         AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS
@@ -77,7 +78,7 @@
     private boolean mIsInPool;
 
     boolean mSealed;
-    int mBooleanProperties;
+    int mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY;
     int mCurrentItemIndex = UNDEFINED;
     int mItemCount = UNDEFINED;
     int mFromIndex = UNDEFINED;
@@ -134,6 +135,8 @@
      */
     public void setSource(View root, int virtualDescendantId) {
         enforceNotSealed();
+        final boolean important = (root != null) ? root.isImportantForAccessibility() : true;
+        setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, important);
         mSourceWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
         final int rootViewId = (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
         mSourceNodeId = AccessibilityNodeInfo.makeNodeId(rootViewId, virtualDescendantId);
@@ -274,6 +277,23 @@
     }
 
     /**
+     * Gets if the source is important for accessibility.
+     *
+     * <strong>Note:</strong> Used only internally to determine whether
+     * to deliver the event to a given accessibility service since some
+     * services may want to regard all views for accessibility while others
+     * may want to regard only the important views for accessibility.
+     *
+     * @return True if the source is important for accessibility,
+     *        false otherwise.
+     *
+     * @hide
+     */
+    public boolean isImportantForAccessibility() {
+        return getBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY);
+    }
+
+    /**
      * Gets the number of items that can be visited.
      *
      * @return The number of items.
@@ -755,7 +775,7 @@
      */
     void clear() {
         mSealed = false;
-        mBooleanProperties = 0;
+        mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY;
         mCurrentItemIndex = UNDEFINED;
         mItemCount = UNDEFINED;
         mFromIndex = UNDEFINED;
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index fc3651c..8182d29 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -28,18 +28,26 @@
 oneway interface IAccessibilityInteractionConnection {
 
     void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback, int prefetchFlags,
-        int interrogatingPid, long interrogatingTid);
+        IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
+        long interrogatingTid);
 
     void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int id, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback,
-        int interrogatingPid, long interrogatingTid);
+        IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
+        long interrogatingTid);
 
     void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
+        IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
+        long interrogatingTid);
+
+    void findFocus(long accessibilityNodeId, int interactionId, int focusType,
+        IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
+        long interrogatingTid);
+
+    void focusSearch(long accessibilityNodeId, int interactionId, int direction,
+        IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
         long interrogatingTid);
 
     void performAccessibilityAction(long accessibilityNodeId, int action, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
+        IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
         long interrogatingTid);
 }
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 320c75d..5b5134a 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -19,7 +19,7 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceConnection;
-import android.accessibilityservice.IEventListener;
+import android.accessibilityservice.IAccessibilityServiceClient;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -49,7 +49,8 @@
 
     void removeAccessibilityInteractionConnection(IWindow windowToken);
 
-    void registerUiTestAutomationService(IEventListener listener, in AccessibilityServiceInfo info);
+    void registerUiTestAutomationService(IAccessibilityServiceClient client,
+        in AccessibilityServiceInfo info);
 
-    void unregisterUiTestAutomationService(IEventListener listener);
+    void unregisterUiTestAutomationService(IAccessibilityServiceClient client);
 }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 057aabe..e68049c 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2062,6 +2062,10 @@
 
             child = mAdapter.getView(position, scrapView, this);
 
+            if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+                child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+            }
+
             if (ViewDebug.TRACE_RECYCLER) {
                 ViewDebug.trace(child, ViewDebug.RecyclerTraceType.BIND_VIEW,
                         position, getChildCount());
@@ -2082,6 +2086,11 @@
             }
         } else {
             child = mAdapter.getView(position, null, this);
+
+            if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+                child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+            }
+
             if (mCacheColorHint != 0) {
                 child.setDrawingCacheBackgroundColor(mCacheColorHint);
             }
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index efdfae3..f279f8e 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -191,6 +191,10 @@
             if (view == null) {
                 // Make a new one
                 view = mAdapter.getView(selectedPosition, null, this);
+
+                if (view.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+                    view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+                }
             }
 
             if (view != null) {
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 97a864c..1a2231e 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -24,14 +24,15 @@
 import android.util.SparseArray;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MotionEvent;
 import android.view.SoundEffectConstants;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 
-
 /**
  * An AdapterView is a view whose children are determined by an {@link Adapter}.
  *
@@ -232,6 +233,11 @@
 
     public AdapterView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+
+        // If not explicitly specified this view is important for accessibility.
+        if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
     }
 
     /**
@@ -643,6 +649,11 @@
     public void setEmptyView(View emptyView) {
         mEmptyView = emptyView;
 
+        // If not explicitly specified this view is important for accessibility.
+        if (emptyView.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            emptyView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
+
         final T adapter = getAdapter();
         final boolean empty = ((adapter == null) || adapter.isEmpty());
         updateEmptyStatus(empty);
@@ -846,12 +857,14 @@
                 }
             } else {
                 fireOnSelected();
+                performAccessibilityActionsOnSelected();
             }
         }
     }
 
     void selectionChanged() {
-        if (mOnItemSelectedListener != null) {
+        if (mOnItemSelectedListener != null
+                || AccessibilityManager.getInstance(mContext).isEnabled()) {
             if (mInLayout || mBlockLayoutRequests) {
                 // If we are in a layout traversal, defer notification
                 // by posting. This ensures that the view tree is
@@ -863,20 +876,16 @@
                 post(mSelectionNotifier);
             } else {
                 fireOnSelected();
+                performAccessibilityActionsOnSelected();
             }
         }
-
-        // we fire selection events here not in View
-        if (mSelectedPosition != ListView.INVALID_POSITION && isShown() && !isInTouchMode()) {
-            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
-        }
     }
 
     private void fireOnSelected() {
-        if (mOnItemSelectedListener == null)
+        if (mOnItemSelectedListener == null) {
             return;
-
-        int selection = this.getSelectedItemPosition();
+        }
+        final int selection = getSelectedItemPosition();
         if (selection >= 0) {
             View v = getSelectedView();
             mOnItemSelectedListener.onItemSelected(this, v, selection,
@@ -886,6 +895,17 @@
         }
     }
 
+    private void performAccessibilityActionsOnSelected() {
+        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+            return;
+        }
+        final int position = getSelectedItemPosition();
+        if (position >= 0) {
+            // we fire selection events here not in View
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+        }
+    }
+
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         View selectedView = getSelectedView();
@@ -936,6 +956,24 @@
         event.setItemCount(getCount());
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public boolean onRequestAccessibilityFocusFromHover(float x, float y) {
+        // We prefer to five focus to the child instead of this view.
+        // Usually the children are not actionable for accessibility,
+        // and they will not take accessibility focus, so we give it.
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (isTransformedTouchPointInView(x, y, child, null)) {
+                return child.requestAccessibilityFocus();
+            }
+        }
+        return super.onRequestAccessibilityFocusFromHover(x, y);
+    }
+
     private boolean isScrollableForAccessibility() {
         T adapter = getAdapter();
         if (adapter != null) {
@@ -1012,6 +1050,9 @@
             mNeedSync = false;
             checkSelectionChanged();
         }
+
+        //TODO: Hmm, we do not know the old state so this is sub-optimal
+        notifyAccessibilityStateChanged();
     }
 
     void checkSelectionChanged() {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index bb00049..c557963 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -414,6 +414,10 @@
             // get the fresh child from the adapter
             final View updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
 
+            if (updatedChild.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+                updatedChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+            }
+
             if (mViewsMap.containsKey(index)) {
                 final FrameLayout fl = (FrameLayout) mViewsMap.get(index).view;
                 // add the new child to the frame, if it exists
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index c5066b6..108b720 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -279,8 +279,13 @@
         // re-order the number spinners to match the current date format
         reorderSpinners();
 
-        // set content descriptions
+        // accessibility
         setContentDescriptions();
+
+        // If not explicitly specified this view is important for accessibility.
+        if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
     }
 
     /**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index cbff58c..040a385 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1681,7 +1681,7 @@
         final int itemCount = clipData.getItemCount();
         for (int i=0; i < itemCount; i++) {
             Item item = clipData.getItemAt(i);
-            content.append(item.coerceToText(mTextView.getContext()));
+            content.append(item.coerceToStyledText(mTextView.getContext()));
         }
 
         final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 91e2e497..6c7ea67 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -105,11 +105,11 @@
         super(context);
         initImageView();
     }
-    
+
     public ImageView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
-    
+
     public ImageView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         initImageView();
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index b2321d9..992849d 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -112,8 +112,7 @@
     private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800;
 
     /**
-     * The duration of scrolling to the next/previous value while snapping to
-     * a given position.
+     * The duration of scrolling while snapping to a given position.
      */
     private static final int SNAP_SCROLL_DURATION = 300;
 
@@ -680,6 +679,11 @@
         mAdjustScroller = new Scroller(getContext(), new DecelerateInterpolator(2.5f));
 
         updateInputTextView();
+
+        // If not explicitly specified this view is important for accessibility.
+        if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
     }
 
     @Override
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9867e47..37d9db7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1105,6 +1105,11 @@
         setLongClickable(longClickable);
 
         if (mEditor != null) mEditor.prepareCursorControllers();
+
+        // If not explicitly specified this view is important for accessibility.
+        if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
     }
 
     private void setTypefaceByIndex(int typefaceIndex, int styleIndex) {
@@ -7710,7 +7715,7 @@
         if (clip != null) {
             boolean didFirst = false;
             for (int i=0; i<clip.getItemCount(); i++) {
-                CharSequence paste = clip.getItemAt(i).coerceToText(getContext());
+                CharSequence paste = clip.getItemAt(i).coerceToStyledText(getContext());
                 if (paste != null) {
                     if (!didFirst) {
                         long minMax = prepareSpacesAroundPaste(min, max, paste);
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index bc88b62..18f7a91 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -251,6 +251,11 @@
 
         // set the content descriptions
         setContentDescriptions();
+
+        // If not explicitly specified this view is important for accessibility.
+        if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index a0e125a..37567fd 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -29,9 +29,9 @@
 import android.widget.Toast;
 
 public class PlatLogoActivity extends Activity {
+    Vibrator mZzz;
     Toast mToast;
     ImageView mContent;
-    Vibrator mZzz = new Vibrator();
     int mCount;
     final Handler mHandler = new Handler();
 
@@ -63,7 +63,8 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        
+
+        mZzz = (Vibrator)getSystemService(VIBRATOR_SERVICE);
         mToast = Toast.makeText(this, "Android 4.0: Ice Cream Sandwich", Toast.LENGTH_SHORT);
 
         mContent = new ImageView(this);
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index 77d0c97..6a46929 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -37,6 +37,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Vibrator;
+import android.os.SystemVibrator;
 import android.os.storage.IMountService;
 import android.os.storage.IMountShutdownObserver;
 
@@ -399,7 +400,7 @@
             }
         } else if (SHUTDOWN_VIBRATE_MS > 0) {
             // vibrate before shutting down
-            Vibrator vibrator = new Vibrator();
+            Vibrator vibrator = new SystemVibrator();
             try {
                 vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
             } catch (Exception e) {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index d1aa1ce..dbf6c8e 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -201,4 +201,40 @@
         }
         return array;
     }
+
+    public static int[] appendInt(int[] cur, int val) {
+        if (cur == null) {
+            return new int[] { val };
+        }
+        final int N = cur.length;
+        for (int i = 0; i < N; i++) {
+            if (cur[i] == val) {
+                return cur;
+            }
+        }
+        int[] ret = new int[N + 1];
+        System.arraycopy(cur, 0, ret, 0, N);
+        ret[N] = val;
+        return ret;
+    }
+
+    public static int[] removeInt(int[] cur, int val) {
+        if (cur == null) {
+            return null;
+        }
+        final int N = cur.length;
+        for (int i = 0; i < N; i++) {
+            if (cur[i] == val) {
+                int[] ret = new int[N - 1];
+                if (i > 0) {
+                    System.arraycopy(cur, 0, ret, 0, i);
+                }
+                if (i < (N - 1)) {
+                    System.arraycopy(cur, i + 1, ret, i, N - i - 1);
+                }
+                return ret;
+            }
+        }
+        return cur;
+    }
 }
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 3973344..0c5d5ef 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -150,6 +150,24 @@
      */
     public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4;
 
+    private static final int CMD_TO_STRING_COUNT = CMD_CHANNEL_DISCONNECTED + 1;
+    private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
+    static {
+        sCmdToString[CMD_CHANNEL_HALF_CONNECTED - BASE] = "CMD_CHANNEL_HALF_CONNECTED";
+        sCmdToString[CMD_CHANNEL_FULL_CONNECTION - BASE] = "CMD_CHANNEL_FULL_CONNECTION";
+        sCmdToString[CMD_CHANNEL_FULLY_CONNECTED - BASE] = "CMD_CHANNEL_FULLY_CONNECTED";
+        sCmdToString[CMD_CHANNEL_DISCONNECT - BASE] = "CMD_CHANNEL_DISCONNECT";
+        sCmdToString[CMD_CHANNEL_DISCONNECTED - BASE] = "CMD_CHANNEL_DISCONNECTED";
+    }
+    protected static String cmdToString(int cmd) {
+        cmd -= BASE;
+        if ((cmd >= 0) && (cmd < sCmdToString.length)) {
+            return sCmdToString[cmd];
+        } else {
+            return null;
+        }
+    }
+
     /** Successful status always 0, !0 is an unsuccessful status */
     public static final int STATUS_SUCCESSFUL = 0;
 
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 07496a7..1391ac3 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -20,9 +20,13 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
+import android.text.TextUtils;
 import android.util.Log;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.HashMap;
 import java.util.Vector;
 
@@ -444,9 +448,11 @@
      * The information maintained for a processed message.
      */
     public static class ProcessedMessageInfo {
-        private int what;
-        private State state;
-        private State orgState;
+        private long mTime;
+        private int mWhat;
+        private String mInfo;
+        private State mState;
+        private State mOrgState;
 
         /**
          * Constructor
@@ -455,8 +461,8 @@
          * @param orgState is the first state the received the message but
          * did not processes the message.
          */
-        ProcessedMessageInfo(Message message, State state, State orgState) {
-            update(message, state, orgState);
+        ProcessedMessageInfo(Message msg, String info, State state, State orgState) {
+            update(msg, info, state, orgState);
         }
 
         /**
@@ -465,31 +471,47 @@
          * @param orgState is the first state the received the message but
          * did not processes the message.
          */
-        public void update(Message message, State state, State orgState) {
-            this.what = message.what;
-            this.state = state;
-            this.orgState = orgState;
+        public void update(Message msg, String info, State state, State orgState) {
+            mTime = System.currentTimeMillis();
+            mWhat = msg.what;
+            mInfo = info;
+            mState = state;
+            mOrgState = orgState;
+        }
+
+        /**
+         * @return time stamp
+         */
+        public long getTime() {
+            return mTime;
+        }
+
+        /**
+         * @return msg.what
+         */
+        public long getWhat() {
+            return mWhat;
         }
 
         /**
          * @return the command that was executing
          */
-        public int getWhat() {
-            return what;
+        public String getInfo() {
+            return mInfo;
         }
 
         /**
          * @return the state that handled this message
          */
         public State getState() {
-            return state;
+            return mState;
         }
 
         /**
          * @return the original state that received the message.
          */
         public State getOriginalState() {
-            return orgState;
+            return mOrgState;
         }
 
         /**
@@ -498,26 +520,24 @@
         @Override
         public String toString() {
             StringBuilder sb = new StringBuilder();
-            sb.append("what=");
-            sb.append(what);
+            sb.append("time=");
+            Calendar c = Calendar.getInstance();
+            c.setTimeInMillis(mTime);
+            sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
             sb.append(" state=");
-            sb.append(cn(state));
+            sb.append(mState == null ? "<null>" : mState.getName());
             sb.append(" orgState=");
-            sb.append(cn(orgState));
-            return sb.toString();
-        }
-
-        /**
-         * @return an objects class name
-         */
-        private String cn(Object n) {
-            if (n == null) {
-                return "null";
-            } else {
-                String name = n.getClass().getName();
-                int lastDollar = name.lastIndexOf('$');
-                return name.substring(lastDollar + 1);
+            sb.append(mOrgState == null ? "<null>" : mOrgState.getName());
+            sb.append(" what=");
+            sb.append(mWhat);
+            sb.append("(0x");
+            sb.append(Integer.toHexString(mWhat));
+            sb.append(")");
+            if ( ! TextUtils.isEmpty(mInfo)) {
+                sb.append(" ");
+                sb.append(mInfo);
             }
+            return sb.toString();
         }
     }
 
@@ -542,9 +562,9 @@
         private int mCount = 0;
 
         /**
-         * Constructor
+         * private constructor use add
          */
-        ProcessedMessages() {
+        private ProcessedMessages() {
         }
 
         /**
@@ -599,22 +619,23 @@
         /**
          * Add a processed message.
          *
-         * @param message
+         * @param msg
+         * @param messageInfo to be stored
          * @param state that handled the message
          * @param orgState is the first state the received the message but
          * did not processes the message.
          */
-        void add(Message message, State state, State orgState) {
+        void add(Message msg, String messageInfo, State state, State orgState) {
             mCount += 1;
             if (mMessages.size() < mMaxSize) {
-                mMessages.add(new ProcessedMessageInfo(message, state, orgState));
+                mMessages.add(new ProcessedMessageInfo(msg, messageInfo, state, orgState));
             } else {
                 ProcessedMessageInfo pmi = mMessages.get(mOldestIndex);
                 mOldestIndex += 1;
                 if (mOldestIndex >= mMaxSize) {
                     mOldestIndex = 0;
                 }
-                pmi.update(message, state, orgState);
+                pmi.update(msg, messageInfo, state, orgState);
             }
         }
     }
@@ -894,11 +915,14 @@
             /**
              * Record that we processed the message
              */
-            if (curStateInfo != null) {
-                State orgState = mStateStack[mStateStackTopIndex].state;
-                mProcessedMessages.add(msg, curStateInfo.state, orgState);
-            } else {
-                mProcessedMessages.add(msg, null, null);
+            if (mSm.recordProcessedMessage(msg)) {
+                if (curStateInfo != null) {
+                    State orgState = mStateStack[mStateStackTopIndex].state;
+                    mProcessedMessages.add(msg, mSm.getMessageInfo(msg), curStateInfo.state,
+                            orgState);
+                } else {
+                    mProcessedMessages.add(msg, mSm.getMessageInfo(msg), null, null);
+                }
             }
         }
 
@@ -1546,6 +1570,24 @@
     }
 
     /**
+     * @return true if msg should be saved in ProcessedMessage, default is true.
+     */
+    protected boolean recordProcessedMessage(Message msg) {
+        return true;
+    }
+
+    /**
+     * Return message info to be logged by ProcessedMessageInfo, default
+     * is an empty string. Override if additional information is desired.
+     *
+     * @param msg that was processed
+     * @return information to be logged as a String
+     */
+    protected String getMessageInfo(Message msg) {
+        return "";
+    }
+
+    /**
      * @return if debugging is enabled
      */
     public boolean isDbg() {
@@ -1577,4 +1619,21 @@
         /** Send the complete construction message */
         mSmHandler.completeConstruction();
     }
+
+    /**
+     * Dump the current state.
+     *
+     * @param fd
+     * @param pw
+     * @param args
+     */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(getName() + ":");
+        pw.println(" total messages=" + getProcessedMessagesCount());
+        for (int i=0; i < getProcessedMessagesSize(); i++) {
+            pw.printf(" msg[%d]: %s\n", i, getProcessedMessageInfo(i));
+            pw.flush();
+        }
+        pw.println("curState=" + getCurrentState().getName());
+    }
 }
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 8c05459..7a6f7d9 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -246,6 +246,10 @@
         mHomeLayout.setOnClickListener(mUpClickListener);
         mHomeLayout.setClickable(true);
         mHomeLayout.setFocusable(true);
+
+        if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
     }
 
     @Override
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 244b166..c48b974 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -35,6 +35,7 @@
 
 static const char* const OutOfResourcesException =
     "android/graphics/SurfaceTexture$OutOfResourcesException";
+static const char* const IllegalStateException = "java/lang/IllegalStateException";
 const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTexture";
 
 struct fields_t {
@@ -212,10 +213,16 @@
     surfaceTexture->setDefaultBufferSize(width, height);
 }
 
-static jint SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz)
+static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz)
 {
     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
-    return surfaceTexture->updateTexImage();
+    status_t err = surfaceTexture->updateTexImage();
+    if (err == INVALID_OPERATION) {
+        jniThrowException(env, IllegalStateException, "Unable to update texture contents (see "
+                "logcat for details)");
+    } else if (err < 0) {
+        jniThrowRuntimeException(env, "Error during updateTexImage (see logcat for details)");
+    }
 }
 
 static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz)
@@ -258,7 +265,7 @@
     {"nativeInit",                 "(ILjava/lang/Object;Z)V", (void*)SurfaceTexture_init },
     {"nativeFinalize",             "()V",   (void*)SurfaceTexture_finalize },
     {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
-    {"nativeUpdateTexImage",       "()I",   (void*)SurfaceTexture_updateTexImage },
+    {"nativeUpdateTexImage",       "()V",   (void*)SurfaceTexture_updateTexImage },
     {"nativeDetachFromGLContext",  "()I",   (void*)SurfaceTexture_detachFromGLContext },
     {"nativeAttachToGLContext",    "(I)I",   (void*)SurfaceTexture_attachToGLContext },
     {"nativeGetTransformMatrix",   "([F)V", (void*)SurfaceTexture_getTransformMatrix },
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index e8a3a3b..5cb172b 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -57,7 +57,7 @@
             gInputDeviceClassInfo.ctor, deviceInfo.getId(), deviceInfo.getGeneration(),
             nameObj.get(), descriptorObj.get(),
             deviceInfo.getSources(), deviceInfo.getKeyboardType(),
-            kcmObj.get()));
+            kcmObj.get(), deviceInfo.hasVibrator()));
 
     const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
     for (size_t i = 0; i < ranges.size(); i++) {
@@ -87,7 +87,7 @@
     gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz));
 
     GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz,
-            "<init>", "(IILjava/lang/String;Ljava/lang/String;IILandroid/view/KeyCharacterMap;)V");
+            "<init>", "(IILjava/lang/String;Ljava/lang/String;IILandroid/view/KeyCharacterMap;Z)V");
 
     GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz,
             "addMotionRange", "(IIFFFF)V");
diff --git a/core/res/res/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index 6b06456..78b7d29 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -20,8 +20,8 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:background="#ff000000" android:shareInterpolator="false">
     <scale
-        android:fromXScale="0.95" android:toXScale="1.0"
-        android:fromYScale="0.95" android:toYScale="1.0"
+        android:fromXScale="0.90" android:toXScale="1.0"
+        android:fromYScale="0.90" android:toYScale="1.0"
         android:pivotX="50%p" android:pivotY="50%p"
         android:fillEnabled="true" android:fillBefore="true"
         android:interpolator="@interpolator/decelerate_cubic"
@@ -30,7 +30,7 @@
     <alpha
         android:fromAlpha="0.0" android:toAlpha="1.0"
         android:fillEnabled="true" android:fillBefore="true"
-        android:interpolator="@interpolator/decelerate_quad"
+        android:interpolator="@interpolator/decelerate_quint"
         android:startOffset="@android:integer/config_shortAnimTime"
         android:duration="@android:integer/config_shortAnimTime"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/drawable-nodpi/view_accessibility_focused.9.png b/core/res/res/drawable-nodpi/view_accessibility_focused.9.png
new file mode 100644
index 0000000..f03f575
--- /dev/null
+++ b/core/res/res/drawable-nodpi/view_accessibility_focused.9.png
Binary files differ
diff --git a/core/res/res/raw/accessibility_gestures.bin b/core/res/res/raw/accessibility_gestures.bin
new file mode 100644
index 0000000..1f95e56
--- /dev/null
+++ b/core/res/res/raw/accessibility_gestures.bin
Binary files differ
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 9528a7a..25fc3a0 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -286,10 +286,8 @@
     <string name="permdesc_setOrientation" msgid="3046126619316671476">"Laat die program toe om die skerm se rotasie te eniger tyd te verander. Dit moet nooit vir normale programme nodig wees nie."</string>
     <string name="permlab_setPointerSpeed" msgid="9175371613322562934">"verander wyserspoed"</string>
     <string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Laat die program toe om die muis of stuurpaneel se wyserspoed te eniger tyd te verander. Dit moet nooit vir normale programme nodig wees nie."</string>
-    <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
-    <skip />
-    <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
-    <skip />
+    <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"verander sleutelborduitleg"</string>
+    <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Laat die program toe om die uitleg van die sleutelbord te verander. Behoort nooit vir gewone programme nodig te wees nie."</string>
     <string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"stuur Linux-seine na programme"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Laat die program toe om te versoek dat die voorsiende sein na alle aanhoudende prosesse gestuur word."</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"laat program altyd loop"</string>
@@ -1063,10 +1061,8 @@
     <string name="adb_active_notification_message" msgid="1016654627626476142">"Raak om USB-ontfouting te deaktiveer."</string>
     <string name="select_input_method" msgid="4653387336791222978">"Kies invoermetode"</string>
     <string name="configure_input_methods" msgid="9091652157722495116">"Stel invoermetodes op"</string>
-    <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
-    <skip />
-    <!-- no translation found for hardware (7517821086888990278) -->
-    <skip />
+    <string name="use_physical_keyboard" msgid="6203112478095117625">"Fisiese sleutelbord"</string>
+    <string name="hardware" msgid="7517821086888990278">"Hardeware"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidate"</u></string>
@@ -1193,14 +1189,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Verminder dag"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Vermeerder jaar"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Verminder jaar"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"gekontroleer"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"nie gekontroleer nie"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"gekies"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"nie gekies nie"</string>
-    <string name="switch_on" msgid="551417728476977311">"aan"</string>
-    <string name="switch_off" msgid="7249798614327155088">"af"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"gedruk"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nie gedruk nie"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Kanselleer"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Vee uit"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 747b12d..0764f0e 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"ቀን ቀንስ"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"ዓመት ጨምር"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"ዓመት ቀንስ"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"ታይቷል"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"አልተፈተሸም"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"የተመረጠ"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"አልተመረጠም"</string>
-    <string name="switch_on" msgid="551417728476977311">"በ:"</string>
-    <string name="switch_off" msgid="7249798614327155088">"ውጪ"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"ተጭኗል"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"አልተጫነም።"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ተወው"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ሰርዝ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index e95ea69..cc3cf93 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"تقليل الأيام"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"زيادة الأعوام"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"تقليل الأعوام"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"تم التحديد"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"لم يتم التحديد"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"محدد"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"غير محدد"</string>
-    <string name="switch_on" msgid="551417728476977311">"تشغيل"</string>
-    <string name="switch_off" msgid="7249798614327155088">"إيقاف"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"مضغوط"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"غير مضغوط"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"إلغاء"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"حذف"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index c78ecd9..16cb445 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -286,10 +286,8 @@
     <string name="permdesc_setOrientation" msgid="3046126619316671476">"Дазваляе прыкладанням змяняць паварот экрана ў любы час. Не патрабуецца для звычайных прыкладанняў."</string>
     <string name="permlab_setPointerSpeed" msgid="9175371613322562934">"змена хутк. перамяшч. ўказ."</string>
     <string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Дазваляе прыкладанням змяняць хуткасць курсору мышы або трэкпада ў любы час. Не патрабуецца для звычайных прыкладанняў."</string>
-    <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
-    <skip />
-    <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
-    <skip />
+    <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"змяніць раскладку клавіятуры"</string>
+    <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Дазваляе прыкладанню змяняць раскладку клавіятуры. Не патрабуецца для звычайных прыкладанняў."</string>
     <string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"адправіць сігналы Linux да прыкладанняў"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Дазваляе прыкладанням запытваць адпраўку падаваемага сігнала для ўсiх пастаянных працэсаў."</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"прымусіць прыкладанне працаваць заўсёды"</string>
@@ -1063,10 +1061,8 @@
     <string name="adb_active_notification_message" msgid="1016654627626476142">"Націсніце, каб адключыць адладку USB."</string>
     <string name="select_input_method" msgid="4653387336791222978">"Выберыце метад уводу"</string>
     <string name="configure_input_methods" msgid="9091652157722495116">"Наладзіць метады ўводу"</string>
-    <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
-    <skip />
-    <!-- no translation found for hardware (7517821086888990278) -->
-    <skip />
+    <string name="use_physical_keyboard" msgid="6203112478095117625">"Фізічная клавіятура"</string>
+    <string name="hardware" msgid="7517821086888990278">"Апар. ср."</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" АБВГДЕЁЖЗІЙКЛМНОПРСТУЎФХЦЧШ\'ЫЬЭЮЯ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"кандыдат."</u></string>
@@ -1193,14 +1189,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Паменшыць лічбу дня"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Павялічыць лічбу года"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Паменшыць лічбу года"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"пастаўлены"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"не пастаўлены"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"абрана"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"не абрана"</string>
-    <string name="switch_on" msgid="551417728476977311">"укл."</string>
-    <string name="switch_off" msgid="7249798614327155088">"адключаны"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"націснутая"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"не націснутая"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Адмена"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Выдаліць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 8bcbec3..83f5e41 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Намаляване на дните"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Увеличаване на годините"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Намаляване на годините"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"отметнато"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"не е отметнато"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"избрано"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"не е избрано"</string>
-    <string name="switch_on" msgid="551417728476977311">"включено"</string>
-    <string name="switch_off" msgid="7249798614327155088">"изключено"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"натиснато"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"не е натиснато"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Отказ"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Изтриване"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index fdae718..c72c2dd 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Fes disminuir el dia"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Fes augmentar l\'any"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Fes disminuir l\'any"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"marcat"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"no marcat"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"seleccionat"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"no seleccionat"</string>
-    <string name="switch_on" msgid="551417728476977311">"activat"</string>
-    <string name="switch_off" msgid="7249798614327155088">"desactivat"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"premut"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"no premut"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancel·la"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Suprimeix"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 98b5771..52d10a2 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Ubrat den"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Přidat rok"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Ubrat rok"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"zaškrtnuto"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"nezaškrtnuto"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"Vybráno"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"Nevybráno"</string>
-    <string name="switch_on" msgid="551417728476977311">"zapnuto"</string>
-    <string name="switch_off" msgid="7249798614327155088">"vypnuto"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"stisknuto"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nestisknuto"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Zrušit"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Smazat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index a3c7474..b0a487b 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -286,10 +286,8 @@
     <string name="permdesc_setOrientation" msgid="3046126619316671476">"Tillader, at appen kan ændre skærmretningen på et hvilket som helst tidspunkt. Bør aldrig være nødvendigt for almindelige apps."</string>
     <string name="permlab_setPointerSpeed" msgid="9175371613322562934">"ændre markørens hastighed"</string>
     <string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Tillader, at appen til enhver tid kan ændre musemarkørens hastighed. Bør aldrig være nødvendigt for normale apps."</string>
-    <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
-    <skip />
-    <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
-    <skip />
+    <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"skift tastaturlayout"</string>
+    <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Tillader, at appen må ændre tastaturlayoutet. Bør aldrig være nødvendig for normale apps."</string>
     <string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"sende Linux-signaler til apps"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Tillader, at appen kan anmode om, at det leverede signal sendes til alle vedholdende processer."</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"sørge for, at appen altid kører"</string>
@@ -1063,10 +1061,8 @@
     <string name="adb_active_notification_message" msgid="1016654627626476142">"Tryk for at deaktivere USB-fejlretning."</string>
     <string name="select_input_method" msgid="4653387336791222978">"Vælg inputmetode"</string>
     <string name="configure_input_methods" msgid="9091652157722495116">"Konfigurer inputmetoder"</string>
-    <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
-    <skip />
-    <!-- no translation found for hardware (7517821086888990278) -->
-    <skip />
+    <string name="use_physical_keyboard" msgid="6203112478095117625">"Fysisk tastatur"</string>
+    <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidater"</u></string>
@@ -1193,14 +1189,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Tidligere dag"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Senere år"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Tidligere år"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"markeret"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"ikke markeret"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"udvalgt"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"ikke valgt"</string>
-    <string name="switch_on" msgid="551417728476977311">"tændt"</string>
-    <string name="switch_off" msgid="7249798614327155088">"slukket"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"trykket på"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"ikke trykket på"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuller"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Slet"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ae88920..27b88f4 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Tag verringern"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Jahr verlängern"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Jahr verringern"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"Aktiviert"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"Nicht aktiviert"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"Ausgewählt"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"Nicht ausgewählt"</string>
-    <string name="switch_on" msgid="551417728476977311">"An"</string>
-    <string name="switch_off" msgid="7249798614327155088">"Aus"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"Gedrückt"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"Nicht gedrückt"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Abbrechen"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Löschen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index ce49a84..0a5dd69 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Μείωση ημέρας"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Αύξηση έτους"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Μείωση έτους"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"ελέγχθηκε"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"δεν επιλέχθηκε"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"επιλεγμένο"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"δεν έχει επιλεγεί"</string>
-    <string name="switch_on" msgid="551417728476977311">"ενεργοποίηση"</string>
-    <string name="switch_off" msgid="7249798614327155088">"απενεργοποιημένη"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"πατήθηκε"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"δεν πατήθηκε"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Ακύρωση"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Διαγραφή"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 38b69fe..fe5a173 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -286,10 +286,8 @@
     <string name="permdesc_setOrientation" msgid="3046126619316671476">"Allows the app to change the rotation of the screen at any time. Should never be needed for normal apps."</string>
     <string name="permlab_setPointerSpeed" msgid="9175371613322562934">"change pointer speed"</string>
     <string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Allows the app to change the mouse or touch pad pointer speed at any time. Should never be needed for normal apps."</string>
-    <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
-    <skip />
-    <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
-    <skip />
+    <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"change keyboard layout"</string>
+    <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Allows the app to change the keyboard layout. Should never be needed for normal apps."</string>
     <string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"send Linux signals to apps"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Allows the app to request that the supplied signal be sent to all persistent processes."</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"make app always run"</string>
@@ -1063,10 +1061,8 @@
     <string name="adb_active_notification_message" msgid="1016654627626476142">"Touch to disable USB debugging."</string>
     <string name="select_input_method" msgid="4653387336791222978">"Choose input method"</string>
     <string name="configure_input_methods" msgid="9091652157722495116">"Set up input methods"</string>
-    <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
-    <skip />
-    <!-- no translation found for hardware (7517821086888990278) -->
-    <skip />
+    <string name="use_physical_keyboard" msgid="6203112478095117625">"Physical keyboard"</string>
+    <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidates"</u></string>
@@ -1193,14 +1189,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Decrease day"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Increase year"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Decrease year"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"ticked"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"not ticked"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"selected"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"not selected"</string>
-    <string name="switch_on" msgid="551417728476977311">"on"</string>
-    <string name="switch_off" msgid="7249798614327155088">"off"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"pressed"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"not pressed"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancel"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index d400734..ade6cfb 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reducir día"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar año"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reducir año"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"marcado"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"no marcado"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"seleccionado"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"No se ha seleccionado."</string>
-    <string name="switch_on" msgid="551417728476977311">"Activado"</string>
-    <string name="switch_off" msgid="7249798614327155088">"Desactivado"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"presionado"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"sin presionar"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Eliminar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 6852184..8f50660 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reducir días"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar año"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reducir año"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"seleccionado"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"no seleccionado"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"seleccionado"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"no seleccionado"</string>
-    <string name="switch_on" msgid="551417728476977311">"activado"</string>
-    <string name="switch_off" msgid="7249798614327155088">"desactivado"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"pulsado"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"sin pulsar"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Eliminar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 737ab21..25f3752 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -286,10 +286,8 @@
     <string name="permdesc_setOrientation" msgid="3046126619316671476">"Võimaldab rakendusel muuta ekraani pööramist mis tahes ajal. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
     <string name="permlab_setPointerSpeed" msgid="9175371613322562934">"kursorikiiruse muutmine"</string>
     <string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Võimaldab rakendusel muuta igal ajal hiire- või puutepadjakursori kiirust. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
-    <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
-    <skip />
-    <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
-    <skip />
+    <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"klaviatuuri paigutuse muutmine"</string>
+    <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Lubab rakendusel muuta klaviatuuri paigutust. Tavarakenduste puhul ei peaks kunagi vaja olema."</string>
     <string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"Linuxi signaalide saatmine rakendustele"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Võimaldab rakendusel taotleda edastatud signaali saatmist kõigile püsiprotsessidele."</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"Rakenduste pidev töös hoidmine"</string>
@@ -1063,10 +1061,8 @@
     <string name="adb_active_notification_message" msgid="1016654627626476142">"Puudutage USB-silumise keelamiseks."</string>
     <string name="select_input_method" msgid="4653387336791222978">"Valige sisestusmeetod"</string>
     <string name="configure_input_methods" msgid="9091652157722495116">"Seadista sisestusmeetodid"</string>
-    <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
-    <skip />
-    <!-- no translation found for hardware (7517821086888990278) -->
-    <skip />
+    <string name="use_physical_keyboard" msgid="6203112478095117625">"Füüsiline klaviatuur"</string>
+    <string name="hardware" msgid="7517821086888990278">"Riistvara"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSŠZŽTUVWÕÄÖÜXY"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSŠZŽTUVWÕÄÖÜXY"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidaadid"</u></string>
@@ -1193,14 +1189,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Päeva vähendamine"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aasta suurendamine"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Aasta vähendamine"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"märgitud"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"pole märgitud"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"valitud"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"pole valitud"</string>
-    <string name="switch_on" msgid="551417728476977311">"sees"</string>
-    <string name="switch_off" msgid="7249798614327155088">"väljas"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"vajutatud"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"pole vajutatud"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Tühista"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Kustuta"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 80b273b..d60c59d 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"کاهش روز"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"افزایش سال"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"کاهش سال"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"علامت زده"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"بدون علامت"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"انتخاب شد"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"انتخاب نشده"</string>
-    <string name="switch_on" msgid="551417728476977311">"روشن"</string>
-    <string name="switch_off" msgid="7249798614327155088">"خاموش"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"فشرده شد"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"فشرده نشد"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"لغو"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index f011ec3..211fab2 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Vähennä päivien määrää."</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Lisää vuosien määrää."</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Vähennä vuosien määrää."</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"valittu"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"ei valittu"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"valittu"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"ei valittu"</string>
-    <string name="switch_on" msgid="551417728476977311">"käytössä"</string>
-    <string name="switch_off" msgid="7249798614327155088">"pois käytöstä"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"painettu"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"ei painettu"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Peruuta"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Poista"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 7c78654..787bdda 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -286,10 +286,8 @@
     <string name="permdesc_setOrientation" msgid="3046126619316671476">"Permet à l\'application de changer l\'orientation de l\'écran à tout moment. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
     <string name="permlab_setPointerSpeed" msgid="9175371613322562934">"changer la vitesse du pointeur"</string>
     <string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Permet à l\'application de modifier à tout moment la vitesse du pointeur de la souris ou du pavé tactile. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
-    <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
-    <skip />
-    <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
-    <skip />
+    <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"changer disposition clavier"</string>
+    <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Permet à l\'application de changer la disposition du clavier. Les applications standards ne devraient pas nécessiter cette autorisation."</string>
     <string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"envoyer des signaux Linux aux applications"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Permet à l\'application de demander que le signal fourni soit envoyé à tous les processus persistants."</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"exécuter l\'application en continu"</string>
@@ -1063,10 +1061,8 @@
     <string name="adb_active_notification_message" msgid="1016654627626476142">"Appuyez pour désactiver le débogage USB."</string>
     <string name="select_input_method" msgid="4653387336791222978">"Sélectionnez le mode de saisie"</string>
     <string name="configure_input_methods" msgid="9091652157722495116">"Configurer les modes de saisie"</string>
-    <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
-    <skip />
-    <!-- no translation found for hardware (7517821086888990278) -->
-    <skip />
+    <string name="use_physical_keyboard" msgid="6203112478095117625">"Clavier physique"</string>
+    <string name="hardware" msgid="7517821086888990278">"Matériel"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidats"</u></string>
@@ -1193,14 +1189,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Jour précédent"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Année suivante"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Année précédente"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"coché"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"non coché"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"sélectionné"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"non sélectionné"</string>
-    <string name="switch_on" msgid="551417728476977311">"activé"</string>
-    <string name="switch_off" msgid="7249798614327155088">"désactivé"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"sélectionné"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"non sélectionné"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuler"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Supprimer"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 1ee3d03..1a95f32 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"दिन कम करें"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"वर्ष बढ़ाएं"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"वर्ष कम करें"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"चेक किया गया"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"चेक नहीं किया गया"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"चयनित"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"चयनित नहीं"</string>
-    <string name="switch_on" msgid="551417728476977311">"चालू"</string>
-    <string name="switch_off" msgid="7249798614327155088">"बंद"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"दबाया गया"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"दबाया नहीं गया."</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"रद्द करें"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"हटाएं"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 0023310..0591e04 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Smanjenje dana"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Povećanje godine"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Smanjenje godine"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"označeno"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"nije označeno"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"odabran"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"nije odabrano"</string>
-    <string name="switch_on" msgid="551417728476977311">"uključeno"</string>
-    <string name="switch_off" msgid="7249798614327155088">"isključeno"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"pritisnut"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nije pritisnut"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Odustani"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Izbriši"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 4ae52d5..4043aef 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Dátum értékének csökkentése"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Év értékének növelése"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Év értékének csökkentése"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"bejelölve"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"nincs bejelölve"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"bejelölve"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"nincs kiválasztva"</string>
-    <string name="switch_on" msgid="551417728476977311">"be"</string>
-    <string name="switch_off" msgid="7249798614327155088">"kikapcsolva"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"megnyomva"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nincs megnyomva"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Mégse"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index bd6294b..7a52979 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -109,7 +109,7 @@
     <string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Tidak diteruskan"</string>
     <string name="fcComplete" msgid="3118848230966886575">"Kode fitur selesai."</string>
     <string name="fcError" msgid="3327560126588500777">"Masalah sambungan atau kode fitur tidak valid."</string>
-    <string name="httpErrorOk" msgid="1191919378083472204">"OK"</string>
+    <string name="httpErrorOk" msgid="1191919378083472204">"Oke"</string>
     <string name="httpError" msgid="7956392511146698522">"Terjadi kesalahan jaringan."</string>
     <string name="httpErrorLookup" msgid="4711687456111963163">"Tidak dapat menemukan URL."</string>
     <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"Skema autentikasi situs tidak didukung."</string>
@@ -744,7 +744,7 @@
     <string name="factorytest_reboot" msgid="6320168203050791643">"Mulai ulang"</string>
     <string name="js_dialog_title" msgid="1987483977834603872">"Laman pada \"<xliff:g id="TITLE">%s</xliff:g>\" menyatakan:"</string>
     <string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
-    <string name="js_dialog_before_unload" msgid="730366588032430474">"Beranjak dari laman ini?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Sentuh OK untuk melanjutkan atau Batal untuk tetap pada laman ini."</string>
+    <string name="js_dialog_before_unload" msgid="730366588032430474">"Beranjak dari laman ini?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Sentuh Oke untuk melanjutkan atau Batal untuk tetap pada laman ini."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Konfirmasi"</string>
     <string name="double_tap_toast" msgid="4595046515400268881">"Kiat: Ketuk dua kali untuk memperbesar dan memperkecil."</string>
     <string name="autofill_this_form" msgid="4616758841157816676">"Isiotomatis"</string>
@@ -892,7 +892,7 @@
     <string name="VideoView_error_title" msgid="3534509135438353077">"Masalah video"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Video ini tidak valid untuk pengaliran ke perangkat ini."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Tidak dapat memutar video ini."</string>
-    <string name="VideoView_error_button" msgid="2822238215100679592">"OK"</string>
+    <string name="VideoView_error_button" msgid="2822238215100679592">"Oke"</string>
     <string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="noon" msgid="7245353528818587908">"tengah hari"</string>
     <string name="Noon" msgid="3342127745230013127">"Tengah hari"</string>
@@ -916,9 +916,9 @@
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Ruang penyimpanan tinggal sedikit"</string>
     <string name="low_internal_storage_view_text" product="tablet" msgid="4231085657068852042">"Ruang penyimpanan tablet semakin sedikit."</string>
     <string name="low_internal_storage_view_text" product="default" msgid="635106544616378836">"Ruang penyimpanan ponsel tersisa sedikit."</string>
-    <string name="ok" msgid="5970060430562524910">"OK"</string>
+    <string name="ok" msgid="5970060430562524910">"Oke"</string>
     <string name="cancel" msgid="6442560571259935130">"Batal"</string>
-    <string name="yes" msgid="5362982303337969312">"OK"</string>
+    <string name="yes" msgid="5362982303337969312">"Oke"</string>
     <string name="no" msgid="5141531044935541497">"Batal"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Perhatian"</string>
     <string name="loading" msgid="7933681260296021180">"Memuat..."</string>
@@ -938,7 +938,7 @@
     <string name="anr_activity_process" msgid="5776209883299089767">"Aktivitas <xliff:g id="ACTIVITY">%1$s</xliff:g> tidak menanggapi."\n\n"Anda ingin menutupnya?"</string>
     <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> tidak menanggapi. Anda ingin menutupnya?"</string>
     <string name="anr_process" msgid="6513209874880517125">"Proses <xliff:g id="PROCESS">%1$s</xliff:g> tidak menanggapi."\n\n"Anda ingin menutupnya?"</string>
-    <string name="force_close" msgid="8346072094521265605">"OK"</string>
+    <string name="force_close" msgid="8346072094521265605">"Oke"</string>
     <string name="report" msgid="4060218260984795706">"Laporkan"</string>
     <string name="wait" msgid="7147118217226317732">"Tunggu"</string>
     <string name="webpage_unresponsive" msgid="3272758351138122503">"Laman ini tidak menanggapi."\n\n"Apakah Anda ingin menutupnya?"</string>
@@ -1011,8 +1011,8 @@
     <string name="select_character" msgid="3365550120617701745">"Sisipkan huruf"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Apl tak dikenal"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Mengirim pesan SMS"</string>
-    <string name="sms_control_message" msgid="4073755190243093924">"Pesan SMS dalam jumlah besar sedang dikirimkan. Sentuh OK untuk melanjutkan, atau Batal untuk menghentikan pengiriman."</string>
-    <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
+    <string name="sms_control_message" msgid="4073755190243093924">"Pesan SMS dalam jumlah besar sedang dikirimkan. Sentuh Oke untuk melanjutkan, atau Batal untuk menghentikan pengiriman."</string>
+    <string name="sms_control_yes" msgid="2532062172402615953">"Oke"</string>
     <string name="sms_control_no" msgid="1715320703137199869">"Batal"</string>
     <string name="sim_removed_title" msgid="6227712319223226185">"Kartu SIM dihapus"</string>
     <string name="sim_removed_message" msgid="2333164559970958645">"Jaringan seluler tidak akan tersedia sampai Anda memulai lagi dengan memasukkan kartu SIM yang valid."</string>
@@ -1048,7 +1048,7 @@
     <string name="dlg_confirm_kill_storage_users_title" msgid="963039033470478697">"Hidupkan penyimpanan USB"</string>
     <string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"Jika Anda menyalakan penyimpanan USB, beberapa apl yang Anda gunakan akan berhenti dan mungkin tidak dapat dibuka hingga penyimpanan USB dimatikan."</string>
     <string name="dlg_error_title" msgid="7323658469626514207">"Operasi USB gagal"</string>
-    <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
+    <string name="dlg_ok" msgid="7376953167039865701">"Oke"</string>
     <string name="usb_mtp_notification_title" msgid="3699913097391550394">"Tersambung sebagai perangkat media"</string>
     <string name="usb_ptp_notification_title" msgid="1960817192216064833">"Tersambung sebagai kamera"</string>
     <string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"Tersambung sebagai pemasang"</string>
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Kurangi hari"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Tambah tahun"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Kurangi tahun"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"dicentang"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"tidak diperiksa"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"dipilih"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"tidak dipilih"</string>
-    <string name="switch_on" msgid="551417728476977311">"nyala"</string>
-    <string name="switch_off" msgid="7249798614327155088">"mati"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"ditekan"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"tidak ditekan"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Batal"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Hapus"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ae52c62..9073bf1 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -286,10 +286,8 @@
     <string name="permdesc_setOrientation" msgid="3046126619316671476">"Consente all\'applicazione di cambiare la rotazione dello schermo in qualsiasi momento. Non dovrebbe mai essere necessario per le normali applicazioni."</string>
     <string name="permlab_setPointerSpeed" msgid="9175371613322562934">"cambio velocità del puntatore"</string>
     <string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Consente all\'applicazione di modificare la velocità del puntatore del mouse o del trackpad in qualsiasi momento. Non dovrebbe mai essere necessaria per le applicazioni normali."</string>
-    <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
-    <skip />
-    <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
-    <skip />
+    <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"modifica del layout tastiera"</string>
+    <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Consente all\'applicazione di modificare il layout della tastiera. Non dovrebbe mai essere necessaria per le normali applicazioni."</string>
     <string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"invio segnali Linux alle applicazioni"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Consente all\'applicazione di richiedere l\'invio del segnale fornito a tutti i processi persistenti."</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"esecuzione permanente delle applicazioni"</string>
@@ -1063,10 +1061,8 @@
     <string name="adb_active_notification_message" msgid="1016654627626476142">"Tocca per disattivare il debug USB."</string>
     <string name="select_input_method" msgid="4653387336791222978">"Scegli il metodo di immissione"</string>
     <string name="configure_input_methods" msgid="9091652157722495116">"Configura metodi di immissione"</string>
-    <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
-    <skip />
-    <!-- no translation found for hardware (7517821086888990278) -->
-    <skip />
+    <string name="use_physical_keyboard" msgid="6203112478095117625">"Tastiera fisica"</string>
+    <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidati"</u></string>
@@ -1193,14 +1189,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Riduci giorno"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumenta anno"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Riduci anno"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"selezionata"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"non selezionato"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"selezionato"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"non selezionato"</string>
-    <string name="switch_on" msgid="551417728476977311">"attivo"</string>
-    <string name="switch_off" msgid="7249798614327155088">"disattivo"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"premuto"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"non premuto"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annulla"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Canc"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 613410f..714f203 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"הפחת יום"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"הוסף שנה"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"הפחת שנה"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"מסומן"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"לא מסומן"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"נבחר"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"לא נבחר"</string>
-    <string name="switch_on" msgid="551417728476977311">"מופעל"</string>
-    <string name="switch_off" msgid="7249798614327155088">"כבוי"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"לחוץ"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"לא לחוץ"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ביטול"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"מחק"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 5e5010d..a5db38b 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"1日戻します"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"1年進めます"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"1年戻します"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"ON"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"OFF"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"ON"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"選択されていません"</string>
-    <string name="switch_on" msgid="551417728476977311">"ON"</string>
-    <string name="switch_off" msgid="7249798614327155088">"OFF"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"ON"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"OFF"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"キャンセル"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"削除"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 5556aae..4f580ba 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"\'일\'을 줄입니다."</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"\'연도\'를 늘립니다."</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"\'연도\'를 줄입니다."</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"확인"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"선택 안함"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"선택됨"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"선택 안함"</string>
-    <string name="switch_on" msgid="551417728476977311">"켜짐"</string>
-    <string name="switch_off" msgid="7249798614327155088">"꺼짐"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"누름"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"누르지 않음"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt 키"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"취소"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete 키"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 9e82e8c..c588fa6 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Sumažinti dienų skaičių"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Padidinti metų skaičių"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Sumažinti metų skaičių"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"pažymėtas"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"nepatikrinta"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"pasirinkta"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"nepasirinkta"</string>
-    <string name="switch_on" msgid="551417728476977311">"įjungta"</string>
-    <string name="switch_off" msgid="7249798614327155088">"išjungta"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"paspausta"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nepaspausta"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Atšaukti"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ištrinti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index ae451e0..d710347 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Norādīt agrāku dienu"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Norādīt vēlāku gadu"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Norādīt agrāku gadu"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"atzīmēta"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"nav atzīmēta"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"atlasīta"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"nav atlasīta"</string>
-    <string name="switch_on" msgid="551417728476977311">"ieslēgts"</string>
-    <string name="switch_off" msgid="7249798614327155088">"izslēgts"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"nospiesta"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nav nospiesta"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alternēšanas taustiņš"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Atcelt"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Dzēšanas taustiņš"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index ead175b..38fc473 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Kurangkan hari"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Tingkatkan tahun"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Kurangkan tahun"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"ditandakan"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"tidak ditandakan"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"dipilih"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"tidak dipilih"</string>
-    <string name="switch_on" msgid="551417728476977311">"hidup"</string>
-    <string name="switch_off" msgid="7249798614327155088">"mati"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"ditekan."</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"tidak ditekan"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Batal"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Padam"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index bbd846f..df05f01 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reduser dager"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Øk år"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduser år"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"valgt"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"ikke valgt"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"valgt"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"ikke valgt"</string>
-    <string name="switch_on" msgid="551417728476977311">"på"</string>
-    <string name="switch_off" msgid="7249798614327155088">"av"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"trykket"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"ikke trykket"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Avbryt"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Slett"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 8440d62..3d48936 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Lagere waarde voor dag"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Hogere waarde voor jaar"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Lagere waarde voor jaar"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"aangevinkt"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"niet aangevinkt"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"geselecteerd"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"niet geselecteerd"</string>
-    <string name="switch_on" msgid="551417728476977311">"aan"</string>
-    <string name="switch_off" msgid="7249798614327155088">"uit"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"ingedrukt"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"niet ingedrukt"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuleren"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ee1e6a3..d566c14 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Zmień dzień na wcześniejszy"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Zmień rok na późniejszy"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Zmień rok na wcześniejszy"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"zaznaczono"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"nie zaznaczono"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"wybrano"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"nie wybrano"</string>
-    <string name="switch_on" msgid="551417728476977311">"włączono"</string>
-    <string name="switch_off" msgid="7249798614327155088">"wyłączono"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"naciśnięto"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nie naciśnięto"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Anuluj"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 393ba17..0f55711 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Diminuir dia"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar ano"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Diminuir ano"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"marcado"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"desmarcado"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"selecionado"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"não selecionado"</string>
-    <string name="switch_on" msgid="551417728476977311">"ativado"</string>
-    <string name="switch_off" msgid="7249798614327155088">"desativado"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"premido"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"não premido"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index bb145f7..1beb4e7 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Diminuir dia"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar ano"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Diminuir ano"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"verificado"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"não selecionado"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"selecionado"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"Não selecionado"</string>
-    <string name="switch_on" msgid="551417728476977311">"ativado"</string>
-    <string name="switch_off" msgid="7249798614327155088">"desativado"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"pressionado"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"não pressionado"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Excluir"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index 8b0718f..637e4b1 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1788,22 +1788,6 @@
     <skip />
     <!-- no translation found for date_picker_decrement_year_button (4482021813491121717) -->
     <skip />
-    <!-- no translation found for checkbox_checked (7222044992652711167) -->
-    <skip />
-    <!-- no translation found for checkbox_not_checked (5174639551134444056) -->
-    <skip />
-    <!-- no translation found for radiobutton_selected (8603599808486581511) -->
-    <skip />
-    <!-- no translation found for radiobutton_not_selected (2908760184307722393) -->
-    <skip />
-    <!-- no translation found for switch_on (551417728476977311) -->
-    <skip />
-    <!-- no translation found for switch_off (7249798614327155088) -->
-    <skip />
-    <!-- no translation found for togglebutton_pressed (4180411746647422233) -->
-    <skip />
-    <!-- no translation found for togglebutton_not_pressed (4495147725636134425) -->
-    <skip />
     <!-- no translation found for keyboardview_keycode_alt (4856868820040051939) -->
     <skip />
     <!-- no translation found for keyboardview_keycode_cancel (1203984017245783244) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index d93d8d1..0f773a5 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reduceţi valoarea pentru zi"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Creşteţi valoarea pentru an"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduceţi valoarea pentru an"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"bifată"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"nebifată"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"selectat"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"neselectat"</string>
-    <string name="switch_on" msgid="551417728476977311">"activat"</string>
-    <string name="switch_off" msgid="7249798614327155088">"dezactivat"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"apăsat"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"neapăsat"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Anulaţi"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ştergeţi"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 2743763..7b7c95bd 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"На день назад"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"На год вперед"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"На год назад"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"установлено"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"не установлено"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"выбрано"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"не выбрано"</string>
-    <string name="switch_on" msgid="551417728476977311">"Включено"</string>
-    <string name="switch_off" msgid="7249798614327155088">"Выкл."</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"нажато"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"не нажато"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Клавиша ALT"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Отмена"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Клавиша удаления"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 49d04c3..a2ac96e 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Ubrať deň"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Pridať rok"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Ubrať rok"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"začiarknuté"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"nezačiarknuté"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"vybratý"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"nie je vybraté"</string>
-    <string name="switch_on" msgid="551417728476977311">"zapnuté"</string>
-    <string name="switch_off" msgid="7249798614327155088">"vypnuté"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"stlačené"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nestlačené"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Zrušiť"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Odstrániť"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 55f3593..2b452ae 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Zmanjšanje vrednosti za dan"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Povečanje vrednosti za leto"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Zmanjšanje vrednosti za leto"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"potrjeno"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"ni odkljukano"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"izbrano"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"ni izbrano"</string>
-    <string name="switch_on" msgid="551417728476977311">"vklopljeno"</string>
-    <string name="switch_off" msgid="7249798614327155088">"izklopljeno"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"vklopljen"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"izklopljen"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Tipka Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Prekliči"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Tipka Delete"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index de03bec..0fdce67 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Смањивање дана"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Повећавање године"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Смањивање године"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"изабрано"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"није потврђено"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"изабрано"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"није изабрано"</string>
-    <string name="switch_on" msgid="551417728476977311">"укључено"</string>
-    <string name="switch_off" msgid="7249798614327155088">"искључено"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"притиснуто"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"није притиснуто"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Откажи"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Избриши"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 7448ff8..2c4efc5 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Minska dagar"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Öka år"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Minska år"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"markerat"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"inte markerat"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"markerade"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"ej vald"</string>
-    <string name="switch_on" msgid="551417728476977311">"på"</string>
-    <string name="switch_off" msgid="7249798614327155088">"av"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"intryckt"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"inte intryckt"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Avbryt"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index ab6c82f..41345bd 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Punguza siku"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Ongeza mwaka"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Punguza mwaka"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"imeangaliwa"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"haijakaguliwa"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"Iliyochaguliwa"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"Haijachaguliwa"</string>
-    <string name="switch_on" msgid="551417728476977311">"Washa"</string>
-    <string name="switch_off" msgid="7249798614327155088">"zima"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"iliyobonyezwa"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"Haijabonyezwa"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Ghairi"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Futa"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 7007522..3f479e5 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"ลดวัน"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"เพิ่มปี"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"ลดปี"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"เลือกไว้"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"ไม่ได้ตรวจสอบ"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"เลือกแล้ว"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"ไม่ได้เลือก"</string>
-    <string name="switch_on" msgid="551417728476977311">"เปิด"</string>
-    <string name="switch_off" msgid="7249798614327155088">"ปิด"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"กดแล้ว"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"ไม่ได้กด"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ยกเลิก"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ลบ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 31ad6c8..a5d3f08 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Bawasan ang araw"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Dagdagdan ang taon"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Bawasan ang taon"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"nilagyan ng check"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"hindi nilagyan ng check"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"pinili"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"Hindi pinili"</string>
-    <string name="switch_on" msgid="551417728476977311">"naka-on"</string>
-    <string name="switch_off" msgid="7249798614327155088">"naka-off"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"pinindot"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"hindi pinindot"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Kanselahin"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Tanggalin"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 959d8bd..86c1a70 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Günü azalt"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Yılı artır"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Yılı azalt"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"işaretli"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"işaretlenmedi"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"seçili"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"seçili değil"</string>
-    <string name="switch_on" msgid="551417728476977311">"açık"</string>
-    <string name="switch_off" msgid="7249798614327155088">"kapalı"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"basıldı"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"basılmadı"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"İptal"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Sil"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index f9d372e..154468c 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"На день назад"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"На рік уперед"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"На рік назад"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"перевірено"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"не перевірено"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"вибрано"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"не вибрано"</string>
-    <string name="switch_on" msgid="551417728476977311">"увімк."</string>
-    <string name="switch_off" msgid="7249798614327155088">"вимкн."</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"натиснуто"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"не натиснуто"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Скасувати"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index dde1ad4..cbad241 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Giảm ngày"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Tăng năm"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Giảm năm"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"đã kiểm tra"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"chưa chọn"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"đã chọn"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"chưa được chọn"</string>
-    <string name="switch_on" msgid="551417728476977311">"bật"</string>
-    <string name="switch_off" msgid="7249798614327155088">"tắt"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"đã bấm"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"chưa được bấm"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Hủy"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Xóa"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 0c338bb..cb612e1 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"减小日期值"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"增大年份值"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"减小年份值"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"已选中"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"未选中"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"已选择"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"未选择"</string>
-    <string name="switch_on" msgid="551417728476977311">"已打开"</string>
-    <string name="switch_off" msgid="7249798614327155088">"已关闭"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"已按下"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"未按下"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"取消"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index c36b1b5..c2ae68e 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"減少日數"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"增加年數"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"減少年數"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"已勾選"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"尚未勾選"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"已選取"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"未選取"</string>
-    <string name="switch_on" msgid="551417728476977311">"開啟"</string>
-    <string name="switch_off" msgid="7249798614327155088">"關閉"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"已按下"</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"未按下"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt 鍵"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"取消"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete 鍵"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 3b3b1e1..eea53ec 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1193,14 +1193,6 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Yehlisa usuku"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Khulisa unyaka"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Yehlisa unyaka"</string>
-    <string name="checkbox_checked" msgid="7222044992652711167">"kuhloliwe"</string>
-    <string name="checkbox_not_checked" msgid="5174639551134444056">"akuhloliwe"</string>
-    <string name="radiobutton_selected" msgid="8603599808486581511">"Okukhethiwe"</string>
-    <string name="radiobutton_not_selected" msgid="2908760184307722393">"akukhethiwe"</string>
-    <string name="switch_on" msgid="551417728476977311">"vuliwe"</string>
-    <string name="switch_off" msgid="7249798614327155088">"valiwe"</string>
-    <string name="togglebutton_pressed" msgid="4180411746647422233">"kucindezelwe."</string>
-    <string name="togglebutton_not_pressed" msgid="4495147725636134425">"akucindezelwe."</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"i-ALT"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Khansela"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Susa"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index aabe407..de24d10 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -835,6 +835,10 @@
 
         <!-- Reference to the Pointer style -->
         <attr name="pointerStyle" format="reference" />
+
+        <!-- The drawable for accessibility focused views. -->
+        <attr name="accessibilityFocusedDrawable" format="reference" />
+
     </declare-styleable>
 
     <!-- **************************************************************** -->
@@ -2123,6 +2127,20 @@
                 layoutDirection is LTR, and ALIGN_LEFT otherwise -->
             <enum name="viewEnd" value="6" />
         </attr>
+
+        <!-- Controls how this View is important for accessibility which is if it fires
+             accessibility events and if it is reported to accessibility services that
+             query the screen. Note: While not recommended, an accessibility service may
+             decide to ignore this attribute and operate on all views in the view tree. -->
+        <attr name="importantForAccessibility" format="integer">
+            <!-- The system determines whether the view is important for accessibility (recommended). -->
+            <enum name="auto" value="0" />
+            <!-- The view is important for accessibility. -->
+            <enum name="yes" value="1" />
+            <!-- The view is not important for accessibility. -->
+            <enum name="no" value="2" />
+        </attr>
+
     </declare-styleable>
 
     <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
@@ -2445,6 +2463,8 @@
         <attr name="accessibilityFlags">
             <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#DEFAULT} -->
             <flag name="flagDefault" value="0x00000001" />
+            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#INCLUDE_NOT_IMPORTANT_VIEWS} -->
+            <flag name="flagIncludeNotImportantViews" value="0x00000002" />
         </attr>
         <!-- Component name of an activity that allows the user to modify
              the settings for this service. This setting cannot be changed at runtime. -->
@@ -4317,6 +4337,7 @@
          <li>"state_hovered"
          <li>"state_drag_can_accept"
          <li>"state_drag_hovered"
+         <li>"state_accessibility_focused"
          </ul>  -->
     <declare-styleable name="DrawableStates">
         <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
@@ -4377,6 +4398,9 @@
              indicating that a drag operation (for which the Drawable's view is a valid recipient)
              is currently positioned over the Drawable. -->
         <attr name="state_drag_hovered" format="boolean" />
+        <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable}
+             indicating that a View has accessibility focus. -->
+        <attr name="state_accessibility_focused" format="boolean" />
     </declare-styleable>
     <declare-styleable name="ViewDrawableStates">
         <attr name="state_pressed" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2006548..4a5e442 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -226,6 +226,7 @@
   <java-symbol type="attr" name="windowFixedWidthMinor" />
   <java-symbol type="attr" name="windowFixedHeightMajor" />
   <java-symbol type="attr" name="windowFixedHeightMinor" />
+  <java-symbol type="attr" name="accessibilityFocusedDrawable"/>
 
   <java-symbol type="bool" name="action_bar_embed_tabs" />
   <java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
@@ -1103,6 +1104,7 @@
   <java-symbol type="xml" name="time_zones_by_country" />
   <java-symbol type="xml" name="sms_short_codes" />
 
+  <java-symbol type="raw" name="accessibility_gestures" />
   <java-symbol type="raw" name="incognito_mode_start_page" />
   <java-symbol type="raw" name="loaderror" />
   <java-symbol type="raw" name="nodomain" />
@@ -3583,7 +3585,11 @@
   <public type="attr" name="layout_marginEnd"/>
   <public type="attr" name="kcm"/>
 
+
   <public type="attr" name="parentActivityName" />
 
   <public type="attr" name="supportsSentenceSpellCheck" />
+
+  <public type="attr" name="importantForAccessibility"/>
+
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 7e06e24..71738ad 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -366,6 +366,9 @@
 
         <!-- Pointer style -->
         <item name="pointerStyle">@android:style/Pointer</item>
+
+        <!-- Accessibility focused drawable. -->
+        <item name="accessibilityFocusedDrawable">@android:drawable/view_accessibility_focused</item>
     </style>
 
     <!-- Variant of {@link #Theme} with no title bar -->
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java b/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
index 3dba4e5..1968a32 100644
--- a/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
+++ b/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
@@ -19,7 +19,7 @@
 import android.widget.focus.ListOfButtons;
 import com.android.frameworks.coretests.R;
 
-import android.test.ActivityInstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.ListAdapter;
 import android.widget.Button;
@@ -31,7 +31,7 @@
  * Tests that focus works as expected when navigating into and out of
  * a {@link ListView} that has buttons in it.
  */
-public class ListOfButtonsTest extends ActivityInstrumentationTestCase<ListOfButtons> {
+public class ListOfButtonsTest extends ActivityInstrumentationTestCase2<ListOfButtons> {
 
     private ListAdapter mListAdapter;
     private Button mButtonAtTop;
@@ -39,7 +39,7 @@
     private ListView mListView;
 
     public ListOfButtonsTest() {
-        super("com.android.frameworks.coretests", ListOfButtons.class);
+        super(ListOfButtons.class);
     }
 
     @Override
@@ -47,6 +47,7 @@
         super.setUp();
 
         ListOfButtons a = getActivity();
+        getInstrumentation().waitForIdleSync();
         mListAdapter = a.getListAdapter();
         mButtonAtTop = (Button) a.findViewById(R.id.button);
         mListView = a.getListView();
diff --git a/docs/html/images/training/backward-compatible-ui-classes-eclair.png b/docs/html/images/training/backward-compatible-ui-classes-eclair.png
new file mode 100644
index 0000000..febba5b
--- /dev/null
+++ b/docs/html/images/training/backward-compatible-ui-classes-eclair.png
Binary files differ
diff --git a/docs/html/images/training/backward-compatible-ui-classes-honeycomb.png b/docs/html/images/training/backward-compatible-ui-classes-honeycomb.png
new file mode 100644
index 0000000..ba14252
--- /dev/null
+++ b/docs/html/images/training/backward-compatible-ui-classes-honeycomb.png
Binary files differ
diff --git a/docs/html/images/training/backward-compatible-ui-classes.png b/docs/html/images/training/backward-compatible-ui-classes.png
new file mode 100644
index 0000000..c5a3cd8
--- /dev/null
+++ b/docs/html/images/training/backward-compatible-ui-classes.png
Binary files differ
diff --git a/docs/html/images/training/backward-compatible-ui-gb.png b/docs/html/images/training/backward-compatible-ui-gb.png
new file mode 100644
index 0000000..621ee63
--- /dev/null
+++ b/docs/html/images/training/backward-compatible-ui-gb.png
Binary files differ
diff --git a/docs/html/images/training/backward-compatible-ui-ics.png b/docs/html/images/training/backward-compatible-ui-ics.png
new file mode 100644
index 0000000..6460554
--- /dev/null
+++ b/docs/html/images/training/backward-compatible-ui-ics.png
Binary files differ
diff --git a/docs/html/images/training/implementing-navigation-up.png b/docs/html/images/training/implementing-navigation-up.png
new file mode 100644
index 0000000..18f3779
--- /dev/null
+++ b/docs/html/images/training/implementing-navigation-up.png
Binary files differ
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index 5297c23..686bde3 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -227,7 +227,31 @@
           </li>
         </ul>
       </li>
-      
+
+      <li class="toggle-list">
+        <div><a href="<?cs var:toroot ?>training/backward-compatible-ui/index.html">
+            <span class="en">Creating Backward-Compatible UIs<span class="new">&nbsp;new!</span></span>
+          </a></div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>training/backward-compatible-ui/abstracting.html">
+            <span class="en">Abstracting the New APIs</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/backward-compatible-ui/new-implementation.html">
+            <span class="en">Proxying to the New APIs</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/backward-compatible-ui/older-implementation.html">
+            <span class="en">Creating an Implementation with Older APIs</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/backward-compatible-ui/using-component.html">
+            <span class="en">Using the Version-Aware Component</span>
+          </a>
+          </li>
+        </ul>
+      </li>
+
       <li class="toggle-list">
         <div><a href="<?cs var:toroot ?>training/enterprise/index.html">
             <span class="en">Developing for Enterprise</span>
@@ -278,7 +302,32 @@
           </a>
           </li>
         </ul>
-       </li>
+      </li>
+
+      <li class="toggle-list">
+        <div><a href="<?cs var:toroot ?>training/implementing-navigation/index.html">
+            <span class="en">Implementing Effective Navigation<span class="new">&nbsp;new!</span></span>
+          </a></div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>training/implementing-navigation/lateral.html">
+            <span class="en">Implementing Lateral Navigation</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/implementing-navigation/ancestral.html">
+            <span class="en">Implementing Ancestral Navigation</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/implementing-navigation/temporal.html">
+            <span class="en">Implementing Temporal Navigation</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/implementing-navigation/descendant.html">
+            <span class="en">Implementing Descendant Navigation</span>
+          </a>
+          </li>
+        </ul>
+      </li>
+
        <li class="toggle-list">
         <div><a href="<?cs var:toroot ?>training/tv/index.html">
            <span class="en">Designing for TV<span class="new">&nbsp;new!</span></span>
diff --git a/docs/html/shareables/training/EffectiveNavigation.zip b/docs/html/shareables/training/EffectiveNavigation.zip
new file mode 100644
index 0000000..f21af45
--- /dev/null
+++ b/docs/html/shareables/training/EffectiveNavigation.zip
Binary files differ
diff --git a/docs/html/shareables/training/TabCompat.zip b/docs/html/shareables/training/TabCompat.zip
new file mode 100644
index 0000000..b70b442
--- /dev/null
+++ b/docs/html/shareables/training/TabCompat.zip
Binary files differ
diff --git a/docs/html/training/backward-compatible-ui/abstracting.jd b/docs/html/training/backward-compatible-ui/abstracting.jd
new file mode 100644
index 0000000..1141b54
--- /dev/null
+++ b/docs/html/training/backward-compatible-ui/abstracting.jd
@@ -0,0 +1,111 @@
+page.title=Abstracting the New APIs
+parent.title=Creating Backward-Compatible UIs
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Proxying to the New APIs
+next.link=new-implementation.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ul>
+  <li><a href="#prepare-abstraction">Prepare for Abstraction</a></li>
+  <li><a href="#create-abstract-tab">Create an Abstract Tab Interface</a></li>
+  <li><a href="#abstract-actionbar-tab">Abstract ActionBar.Tab</a></li>
+  <li><a href="#abstract-actionbar-methods">Abstract ActionBar Tab Methods</a></li>
+</ul>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a></li>
+  <li><a href="{@docRoot}guide/topics/ui/actionbar.html#Tabs">Action Bar Tabs</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/TabCompat.zip"
+  class="button">Download the sample app</a>
+<p class="filename">TabCompat.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Suppose you want to use <a href="{@docRoot}guide/topics/ui/actionbar.html#Tabs">action bar tabs</a> as the primary form of top-level navigation in your application. Unfortunately, the {@link android.app.ActionBar} APIs are only available in Android 3.0 or later (API level 11+). Thus, if you want to distribute your application to devices running earlier versions of the platform, you need to provide an implementation that supports the newer API while providing a fallback mechanism that uses older APIs.</p>
+
+<p>In this class, you build a tabbed user interface (UI) component that uses abstract classes with version-specific implementations to provide backward-compatibility. This lesson describes how to create an abstraction layer for the new tab APIs as the first step toward building the tab component.</p>
+
+<h2 id="prepare-abstraction">Prepare for Abstraction</h2>
+
+<p><a href="http://en.wikipedia.org/wiki/Abstraction_(computer_science)">Abstraction</a> in the Java programming language involves the creation of one or more interfaces or abstract classes to hide implementation details. In the case of newer Android APIs, you can use abstraction to build version-aware components that use the current APIs on newer devices, and fallback to older, more compatible APIs on older devices.</p>
+
+<p>When using this approach, you first determine what newer classes you want to be able to use in a backward compatible way, then create abstract classes, based on the public interfaces of the newer classes. In defining the abstraction interfaces, you should mirror the newer API as much as possible. This maximizes forward-compatibility and makes it easier to drop the abstraction layer in the future when it is no longer necessary.</p>
+
+<p>After creating abstract classes for these new APIs, any number of implementations can be created and chosen at runtime. For the purposes of backward-compatibility, these implementations can vary by required API level. Thus, one implementation may use recently released APIs, while others can use older APIs.</p>
+
+<h2 id="create-abstract-tab">Create an Abstract Tab Interface</h2>
+
+<p>In order to create a backward-compatible version of tabs, you should first determine which features and specific APIs your application requires. In the case of top-level section tabs, suppose you have the following functional requirements:</p>
+
+<ol>
+<li>Tab indicators should show text and an icon.</li>
+<li>Tabs can be associated with a fragment instance.</li>
+<li>The activity should be able to listen for tab changes.</li>
+</ol>
+
+<p>Preparing these requirements in advance allows you to control the scope of your abstraction layer. This means that you can spend less time creating multiple implementations of your abstraction layer and begin using your new backward-compatible implementation sooner.</p>
+
+<p>The key APIs for tabs are in {@link android.app.ActionBar} and {@link android.app.ActionBar.Tab ActionBar.Tab}. These are the APIs to abstract in order to make your tabs version-aware. The requirements for this example project call for compatibility back to Eclair (API level 5) while taking advantage of the new tab features in Honeycomb (API Level 11). A diagram of the class structure to support these two implementations and their abstract base classes (or interfaces) is shown below.</p>
+
+<img src="{@docRoot}images/training/backward-compatible-ui-classes.png"
+  alt="Class diagram of abstract base classes and version-specific implementations." id="figure-classes">
+
+<p class="img-caption"><strong>Figure 1.</strong> Class diagram of abstract base classes and version-specific implementations.</p>
+
+<h2 id="abstract-actionbar-tab">Abstract ActionBar.Tab</h2>
+
+<p>Get started on building your tab abstraction layer by creating an abstract class representing a tab, that mirrors the {@link android.app.ActionBar.Tab ActionBar.Tab} interface:</p>
+
+<pre>
+public abstract class CompatTab {
+    ...
+    public abstract CompatTab setText(int resId);
+    public abstract CompatTab setIcon(int resId);
+    public abstract CompatTab setTabListener(
+            CompatTabListener callback);
+    public abstract CompatTab setFragment(Fragment fragment);
+
+    public abstract CharSequence getText();
+    public abstract Drawable getIcon();
+    public abstract CompatTabListener getCallback();
+    public abstract Fragment getFragment();
+    ...
+}
+</pre>
+
+<p>You can use an abstract class instead of an interface here to simplify the implementation of common features such as association of tab objects with activities (not shown in the code snippet).</p>
+
+<h2 id="abstract-actionbar-methods">Abstract ActionBar Tab Methods</h2>
+
+<p>Next, define an abstract class that allows you to create and add tabs to an activity, like {@link android.app.ActionBar#newTab ActionBar.newTab()} and {@link android.app.ActionBar#addTab ActionBar.addTab()}:</p>
+
+<pre>
+public abstract class TabHelper {
+    ...
+
+    public CompatTab newTab(String tag) {
+        // This method is implemented in a later lesson.
+    }
+
+    public abstract void addTab(CompatTab tab);
+
+    ...
+}
+</pre>
+
+<p>In the next lessons, you create implementations for <code>TabHelper</code> and <code>CompatTab</code> that work across both older and newer platform versions.</p>
diff --git a/docs/html/training/backward-compatible-ui/index.jd b/docs/html/training/backward-compatible-ui/index.jd
new file mode 100644
index 0000000..7e27e68
--- /dev/null
+++ b/docs/html/training/backward-compatible-ui/index.jd
@@ -0,0 +1,57 @@
+page.title=Creating Backward-Compatible UIs
+
+trainingnavtop=true
+startpage=true
+next.title=Abstracting the New Implementation
+next.link=abstracting.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+
+<ul>
+  <li>API level 5</li>
+  <li><a href="{@docRoot}sdk/compatibility-library.html">The Android Support Package</a></li>
+</ul>
+
+<h2>You should also read</h2>
+
+<ul>
+  <li><a href="{@docRoot}resources/samples/ActionBarCompat/index.html">ActionBarCompat</a></li>
+  <li><a href="http://android-developers.blogspot.com/2010/07/how-to-have-your-cupcake-and-eat-it-too.html">How to have your (Cup)cake and eat it too</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/TabCompat.zip"
+  class="button">Download the sample app</a>
+<p class="filename">TabCompat.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>This class demonstrates how to use UI components and APIs available in newer versions of Android in a backward-compatible way, ensuring that your application still runs on previous versions of the platform.</p>
+
+<p>Throughout this class, the new <a href="{@docRoot}guide/topics/ui/actionbar.html#Tabs">Action Bar Tabs</a> feature introduced in Android 3.0 (API level 11) serves as the guiding example, but you can apply these techniques to other UI components and API features.</p>
+
+<h2 id="lessons">Lessons</h2>
+
+
+<dl>
+  <dt><strong><a href="abstracting.html">Abstracting the New APIs</a></strong></dt>
+    <dd>Determine which features and APIs your application needs. Learn how to define application-specific, intermediary Java interfaces that abstract the implementation of the UI component to your application.</dd>
+
+  <dt><strong><a href="new-implementation.html">Proxying to the New APIs</a></strong></dt>
+    <dd>Learn how to create an implementation of your interface that uses newer APIs.</dd>
+
+  <dt><strong><a href="older-implementation.html">Creating an Implementation with Older APIs</a></strong></dt>
+    <dd>Learn how to create a custom implementation of your interface that uses older APIs.</dd>
+
+  <dt><strong><a href="using-component.html">Using the Version-Aware Component</a></strong></dt>
+    <dd>Learn how to choose an implementation to use at runtime, and begin using the interface in your application.</dd>
+</dl>
diff --git a/docs/html/training/backward-compatible-ui/new-implementation.jd b/docs/html/training/backward-compatible-ui/new-implementation.jd
new file mode 100644
index 0000000..5b8b51c
--- /dev/null
+++ b/docs/html/training/backward-compatible-ui/new-implementation.jd
@@ -0,0 +1,113 @@
+page.title=Proxying to the New APIs
+parent.title=Creating Backward-Compatible UIs
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Abstracting the New APIs
+previous.link=abstracting.html
+next.title=Creating an Implementation with Older APIs
+next.link=older-implementation.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+  <li><a href="#new-tabs">Implement Tabs Using New APIs</a></li>
+  <li><a href="#compattabhoneycomb">Implement CompatTabHoneycomb</a></li>
+  <li><a href="#tabhelperhoneycomb">Implement TabHelperHoneycomb</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a></li>
+  <li><a href="{@docRoot}guide/topics/ui/actionbar.html#Tabs">Action Bar Tabs</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/TabCompat.zip"
+  class="button">Download the sample app</a>
+<p class="filename">TabCompat.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>This lesson shows you how to subclass the <code>CompatTab</code> and <code>TabHelper</code> abstract classes and use new APIs. Your application can use this implementation on devices running a platform version that supports them.</p>
+
+<h2 id="new-tabs">Implement Tabs Using New APIs</h2>
+
+<p>The concrete classes for <code>CompatTab</code> and <code>TabHelper</code> that use newer APIs are a <em>proxy</em> implementation. Since the abstract classes defined in the previous lesson mirror the new APIs (class structure, method signatures, etc.), the concrete classes that use these newer APIs simply proxy method calls and their results.</p>
+
+<p>You can directly use newer APIs in these concrete classes&mdash;and not crash on earlier devices&mdash;because of lazy class loading. Classes are loaded and initialized on first access&mdash;instantiating the class or accessing one of its static fields or methods for the first time. Thus, as long as you don't instantiate the Honeycomb-specific implementations on pre-Honeycomb devices, the Dalvik VM won't throw any {@link java.lang.VerifyError} exceptions.</p>
+
+<p>A good naming convention for this implementation is to append the API level or platform version code name corresponding to the APIs required by the concrete classes. For example, the native tab implementation can be provided by <code>CompatTabHoneycomb</code> and <code>TabHelperHoneycomb</code> classes, since they rely on APIs available in Android 3.0 (API level 11) or later.</p>
+
+<img src="{@docRoot}images/training/backward-compatible-ui-classes-honeycomb.png"
+  alt="Class diagram for the Honeycomb implementation of tabs." id="figure-classes">
+
+<p class="img-caption"><strong>Figure 1.</strong> Class diagram for the Honeycomb implementation of tabs.</p>
+
+<h2 id="compattabhoneycomb">Implement CompatTabHoneycomb</h2>
+
+<p><code>CompatTabHoneycomb</code> is the implementation of the <code>CompatTab</code> abstract class that <code>TabHelperHoneycomb</code> uses to reference individual tabs. <code>CompatTabHoneycomb</code> simply proxies all method calls to its contained {@link android.app.ActionBar.Tab} object.</p>
+
+<p>Begin implementing <code>CompatTabHoneycomb</code> using the new {@link android.app.ActionBar.Tab ActionBar.Tab} APIs:</p>
+
+<pre>
+public class CompatTabHoneycomb extends CompatTab {
+    // The native tab object that this CompatTab acts as a proxy for.
+    ActionBar.Tab mTab;
+    ...
+
+    protected CompatTabHoneycomb(FragmentActivity activity, String tag) {
+        ...
+        // Proxy to new ActionBar.newTab API
+        mTab = activity.getActionBar().newTab();
+    }
+
+    public CompatTab setText(int resId) {
+        // Proxy to new ActionBar.Tab.setText API
+        mTab.setText(resId);
+        return this;
+    }
+
+    ...
+    // Do the same for other properties (icon, callback, etc.)
+}
+</pre>
+
+<h2 id="tabhelperhoneycomb">Implement TabHelperHoneycomb</h2>
+
+<p><code>TabHelperHoneycomb</code> is the implementation of the <code>TabHelper</code> abstract class that proxies method calls to an actual {@link android.app.ActionBar}, obtained from its contained {@link android.app.Activity}.</p>
+
+<p>Implement <code>TabHelperHoneycomb</code>, proxying method calls to the {@link android.app.ActionBar} API:</p>
+
+<pre>
+public class TabHelperHoneycomb extends TabHelper {
+    ActionBar mActionBar;
+    ...
+
+    protected void setUp() {
+        if (mActionBar == null) {
+            mActionBar = mActivity.getActionBar();
+            mActionBar.setNavigationMode(
+                    ActionBar.NAVIGATION_MODE_TABS);
+        }
+    }
+
+    public void addTab(CompatTab tab) {
+        ...
+        // Tab is a CompatTabHoneycomb instance, so its
+        // native tab object is an ActionBar.Tab.
+        mActionBar.addTab((ActionBar.Tab) tab.getTab());
+    }
+
+    // The other important method, newTab() is part of
+    // the base implementation.
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/backward-compatible-ui/older-implementation.jd b/docs/html/training/backward-compatible-ui/older-implementation.jd
new file mode 100644
index 0000000..5006123
--- /dev/null
+++ b/docs/html/training/backward-compatible-ui/older-implementation.jd
@@ -0,0 +1,126 @@
+page.title=Creating an Implementation with Older APIs
+parent.title=Creating Backward-Compatible UIs
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Proxying to the New APIs
+previous.link=new-implementation.html
+next.title=Using the Version-Aware Component
+next.link=using-component.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+  <li><a href="#decide-substitute">Decide on a Substitute Solution</a></li>
+  <li><a href="#older-tabs">Implement Tabs Using Older APIs</a></li>
+</ol>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/TabCompat.zip"
+  class="button">Download the sample app</a>
+<p class="filename">TabCompat.zip</p>
+</div>
+
+</div>
+</div>
+
+
+<p>This lesson discusses how to create an implementation that mirrors newer APIs yet supports older devices.</p>
+
+<h2 id="decide-substitute">Decide on a Substitute Solution</h2>
+
+<p>The most challenging task in using newer UI features in a backward-compatible way is deciding on and implementing an older (fallback) solution for older platform versions. In many cases, it's possible to fulfill the purpose of these newer UI components using older UI framework features. For example:</p>
+
+<ul>
+
+<li>
+<p>Action bars can be implemented using a horizontal {@link android.widget.LinearLayout} containing image buttons, either as custom title bars or as views in your activity layout. Overflow actions can be presented under the device <em>Menu</em> button.</p>
+</li>
+
+<li>
+<p>Action bar tabs can be implemented using a horizontal {@link android.widget.LinearLayout} containing buttons, or using the {@link android.widget.TabWidget} UI element.</p>
+</li>
+
+<li>
+<p>{@link android.widget.NumberPicker} and {@link android.widget.Switch} widgets can be implemented using {@link android.widget.Spinner} and {@link android.widget.ToggleButton} widgets, respectively.</p>
+</li>
+
+<li>
+<p>{@link android.widget.ListPopupWindow} and {@link android.widget.PopupMenu} widgets can be implemented using {@link android.widget.PopupWindow} widgets.</p>
+</li>
+
+</ul>
+
+<p>There generally isn't a one-size-fits-all solution for backporting newer UI components to older devices. Be mindful of the user experience: on older devices, users may not be familiar with newer design patterns and UI components. Give some thought as to how the same functionality can be delivered using familiar elements. In many cases this is less of a concern&mdash;if newer UI components are prominent in the application ecosystem (such as the action bar), or where the interaction model is extremely simple and intuitive (such as swipe views using a {@link android.support.v4.view.ViewPager}).</p>
+
+<h2 id="older-tabs">Implement Tabs Using Older APIs</h2>
+
+<p>To create an older implementation of action bar tabs, you can use a {@link android.widget.TabWidget} and {@link android.widget.TabHost} (although one can alternatively use horizontally laid-out {@link android.widget.Button} widgets). Implement this in classes called <code>TabHelperEclair</code> and <code>CompatTabEclair</code>, since this implementation uses APIs introduced no later than Android 2.0 (Eclair).</p>
+
+
+<img src="{@docRoot}images/training/backward-compatible-ui-classes-eclair.png"
+  alt="Class diagram for the Eclair implementation of tabs." id="figure-classes">
+
+<p class="img-caption"><strong>Figure 1.</strong> Class diagram for the Eclair implementation of tabs.</p>
+
+<p>The <code>CompatTabEclair</code> implementation stores tab properties such as the tab text and icon in instance variables, since there isn't an {@link android.app.ActionBar.Tab ActionBar.Tab} object available to handle this storage:</p>
+
+<pre>
+public class CompatTabEclair extends CompatTab {
+    // Store these properties in the instance,
+    // as there is no ActionBar.Tab object.
+    private CharSequence mText;
+    ...
+
+    public CompatTab setText(int resId) {
+        // Our older implementation simply stores this
+        // information in the object instance.
+        mText = mActivity.getResources().getText(resId);
+        return this;
+    }
+
+    ...
+    // Do the same for other properties (icon, callback, etc.)
+}
+</pre>
+
+<p>The <code>TabHelperEclair</code> implementation makes use of methods on the
+{@link android.widget.TabHost} widget for creating {@link android.widget.TabHost.TabSpec}
+objects and tab indicators:</p>
+
+<pre>
+public class TabHelperEclair extends TabHelper {
+    private TabHost mTabHost;
+    ...
+
+    protected void setUp() {
+        if (mTabHost == null) {
+            // Our activity layout for pre-Honeycomb devices
+            // must contain a TabHost.
+            mTabHost = (TabHost) mActivity.findViewById(
+                    android.R.id.tabhost);
+            mTabHost.setup();
+        }
+    }
+
+    public void addTab(CompatTab tab) {
+        ...
+        TabSpec spec = mTabHost
+                .newTabSpec(tag)
+                .setIndicator(tab.getText()); // And optional icon
+        ...
+        mTabHost.addTab(spec);
+    }
+
+    // The other important method, newTab() is part of
+    // the base implementation.
+}
+</pre>
+
+<p>You now have two implementations of <code>CompatTab</code> and <code>TabHelper</code>: one that works on devices running Android 3.0 or later and uses new APIs, and another that works on devices running Android 2.0 or later and uses older APIs. The next lesson discusses using these implementations in your application.</p>
diff --git a/docs/html/training/backward-compatible-ui/using-component.jd b/docs/html/training/backward-compatible-ui/using-component.jd
new file mode 100644
index 0000000..4bf7fa0
--- /dev/null
+++ b/docs/html/training/backward-compatible-ui/using-component.jd
@@ -0,0 +1,143 @@
+page.title=Using the Version-Aware Component
+parent.title=Creating Backward-Compatible UIs
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Creating an Implementation with Older APIs
+previous.link=older-implementation.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+  <li><a href="#switching-logic">Add the Switching Logic</a></li>
+  <li><a href="#layout">Create a Version-Aware Activity Layout</a></li>
+  <li><a href="#use-tabhelper">Use TabHelper in Your Activity</a></li>
+</ol>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/TabCompat.zip"
+  class="button">Download the sample app</a>
+<p class="filename">TabCompat.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Now that you have two implementations of <code>TabHelper</code> and <code>CompatTab</code>&mdash;one for Android 3.0 and later and one for earlier versions of the platform&mdash;it's time to do something with these implementations. This lesson discusses creating the logic for switching between these implementations, creating version-aware layouts, and finally using the backward-compatible UI component.</p>
+
+<h2 id="switching-logic">Add the Switching Logic</h2>
+
+<p>The <code>TabHelper</code> abstract class acts as a <a href="http://en.wikipedia.org/wiki/Factory_(software_concept)">factory</a> for creating version-appropriate <code>TabHelper</code> and <code>CompatTab</code> instances, based on the current device's platform version:</p>
+
+<pre>
+public abstract class TabHelper {
+    ...
+    // Usage is TabHelper.createInstance(activity)
+    public static TabHelper createInstance(FragmentActivity activity) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            return new TabHelperHoneycomb(activity);
+        } else {
+            return new TabHelperEclair(activity);
+        }
+    }
+
+    // Usage is mTabHelper.newTab("tag")
+    public CompatTab newTab(String tag) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            return new CompatTabHoneycomb(mActivity, tag);
+        } else {
+            return new CompatTabEclair(mActivity, tag);
+        }
+    }
+    ...
+}
+</pre>
+
+<h2 id="layout">Create a Version-Aware Activity Layout</h2>
+
+<p>The next step is to provide layouts for your activity that can support the two tab implementations. For the older implementation (<code>TabHelperEclair</code>), you need to ensure that your activity layout contains a {@link android.widget.TabWidget} and {@link android.widget.TabHost}, along with a container for tab contents:</p>
+
+<p><strong>res/layout/main.xml:</strong></p>
+
+<pre>
+&lt;!-- This layout is for API level 5-10 only. --&gt;
+&lt;TabHost xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/tabhost"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"&gt;
+
+    &lt;LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:padding="5dp"&gt;
+
+        &lt;TabWidget
+            android:id="@android:id/tabs"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" /&gt;
+
+        &lt;FrameLayout
+            android:id="@android:id/tabcontent"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1" /&gt;
+
+    &lt;/LinearLayout&gt;
+&lt;/TabHost&gt;
+</pre>
+
+<p>For the <code>TabHelperHoneycomb</code> implementation, all you need is a {@link android.widget.FrameLayout} to contain the tab contents, since the tab indicators are provided by the {@link android.app.ActionBar}:</p>
+
+<p><strong>res/layout-v11/main.xml:</strong></p>
+
+<pre>
+&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/tabcontent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" /&gt;
+</pre>
+
+<p>At runtime, Android will decide which version of the <code>main.xml</code> layout to inflate depending on the platform version. This is the same logic shown in the previous section to determine which <code>TabHelper</code> implementation to use.</p>
+
+<h2 id="use-tabhelper">Use TabHelper in Your Activity</h2>
+
+<p>In your activity's {@link android.app.Activity#onCreate onCreate()} method, you can obtain a <code>TabHelper</code> object and add tabs with the following code:</p>
+
+<pre>
+{@literal @}Override
+public void onCreate(Bundle savedInstanceState) {
+    setContentView(R.layout.main);
+
+    TabHelper tabHelper = TabHelper.createInstance(this);
+    tabHelper.setUp();
+
+    CompatTab photosTab = tabHelper
+            .newTab("photos")
+            .setText(R.string.tab_photos);
+    tabHelper.addTab(photosTab);
+
+    CompatTab videosTab = tabHelper
+            .newTab("videos")
+            .setText(R.string.tab_videos);
+    tabHelper.addTab(videosTab);
+}
+</pre>
+
+<p>When running the application, this code inflates the correct activity layout and instantiates either a <code>TabHelperHoneycomb</code> or <code>TabHelperEclair</code> object. The concrete class that's actually used is opaque to the activity, since they share the common <code>TabHelper</code> interface.</p>
+
+<p>Below are two screenshots of this implementation running on an Android 2.3 and Android 4.0 device.</p>
+
+<img src="{@docRoot}images/training/backward-compatible-ui-gb.png"
+  alt="Example screenshot of tabs running on an Android 2.3 device (using TabHelperEclair)." width="200">
+
+<img src="{@docRoot}images/training/backward-compatible-ui-ics.png"
+  alt="Example screenshots of tabs running on an Android 4.0 device (using TabHelperHoneycomb)." width="200">
+
+<p class="img-caption"><strong>Figure 1.</strong> Example screenshots of backward-compatible tabs running on an Android 2.3 device (using <code>TabHelperEclair</code>) and an Android 4.0 device (using <code>TabHelperHoneycomb</code>).</p>
diff --git a/docs/html/training/implementing-navigation/ancestral.jd b/docs/html/training/implementing-navigation/ancestral.jd
new file mode 100644
index 0000000..495b45d
--- /dev/null
+++ b/docs/html/training/implementing-navigation/ancestral.jd
@@ -0,0 +1,124 @@
+page.title=Implementing Ancestral Navigation
+parent.title=Implementing Effective Navigation
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Implementing Lateral Navigation
+previous.link=lateral.html
+next.title=Implementing Temporal Navigation
+next.link=temporal.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+  <li><a href="#up">Implement <em>Up</em> Navigation</a></li>
+  <li><a href="#app-home">Properly Handle the Application Home Screen</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}training/design-navigation/ancestral-temporal.html">Providing Ancestral and Temporal Navigation</a></li>
+  <li><a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a></li>
+  <li><a href="{@docRoot}design/patterns/navigation.html">Android Design: Navigation</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/EffectiveNavigation.zip"
+  class="button">Download the sample app</a>
+<p class="filename">EffectiveNavigation.zip</p>
+</div>
+
+</div>
+</div>
+
+
+<p><em>Ancestral navigation</em> is up the application's information hierarchy, where the top of the hierarchy (or root) is the application's home screen. This navigation concept is described in <a href="{@docRoot}training/design-navigation/ancestral-temporal.html">Designing Effective Navigation</a>. This lesson discusses how to provide ancestral navigation using the <em>Up</em> button in the action bar.</p>
+
+
+<h2 id="up">Implement <em>Up</em> Navigation</h2>
+
+<p>When implementing ancestral navigation, all screens in your application that aren't the home screen should offer a means of navigating to the immediate parent screen in the hierarchy via the <em>Up</em> button in the action bar.</p>
+
+
+<img src="{@docRoot}images/training/implementing-navigation-up.png"
+  alt="The Up button in the action bar." id="figure-up">
+
+<p class="img-caption"><strong>Figure 1.</strong> The <em>Up</em> button in the action bar.</p>
+
+<p>Regardless of how the current screen was reached, pressing this button should always take the user to the same screen in the hierarchy.</p>
+
+<p>To implement <em>Up</em>, enable it in the action bar in your activity's {@link android.app.Activity#onCreate onCreate()} method:</p>
+
+<pre>
+{@literal @}Override
+public void onCreate(Bundle savedInstanceState) {
+    ...
+    getActionBar().setDisplayHomeAsUpEnabled(true);
+    ...
+}
+</pre>
+
+<p>You should also handle <code>android.R.id.home</code> in {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()}. This resource is the menu item ID for the <em>Home</em> (or <em>Up</em>) button. To ensure that a specific parent activity is shown, <em>DO NOT</em> simply call {@link android.app.Activity#finish finish()}. Instead, use an intent such as the one described below.</p>
+
+<pre>
+{@literal @}Override
+public boolean onOptionsItemSelected(MenuItem item) {
+    switch (item.getItemId()) {
+        case android.R.id.home:
+            // This is called when the Home (Up) button is pressed
+            // in the Action Bar.
+            Intent parentActivityIntent = new Intent(this, MyParentActivity.class);
+            parentActivityIntent.addFlags(
+                    Intent.FLAG_ACTIVITY_CLEAR_TOP |
+                    Intent.FLAG_ACTIVITY_NEW_TASK);
+            startActivity(parentActivityIntent);
+            finish();
+            return true;
+    }
+    return super.onOptionsItemSelected(item);
+}
+</pre>
+
+<p>When the current activity belongs to a task from a different application&mdash;for example if it was reached via an intent from another application&mdash;pressing <em>Up</em> should create a new task for the application with a synthesized back stack. This approach is described in <a href="{@docRoot}design/patterns/navigation.html">Android Design: Navigation</a> and the {@link android.support.v4.app.TaskStackBuilder} class reference.</p>
+
+<p>The {@link android.support.v4.app.NavUtils} and {@link android.support.v4.app.TaskStackBuilder} classes in the <a href="{@docRoot}sdk/compatibility-library.html">Android Support Package</a> provide helpers for implementing this behavior correctly. An example usage of these two helper classes is below:</p>
+
+<pre>
+{@literal @}Override
+public boolean onOptionsItemSelected(MenuItem item) {
+    switch (item.getItemId()) {
+        case android.R.id.home:
+            Intent upIntent = new Intent(this, MyParentActivity.class);
+            if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
+                // This activity is not part of the application's task, so create a new task
+                // with a synthesized back stack.
+                TaskStackBuilder.from(this)
+                        .addNextIntent(new Intent(this, MyGreatGrandParentActivity.class))
+                        .addNextIntent(new Intent(this, MyGrandParentActivity.class))
+                        .addNextIntent(upIntent)
+                        .startActivities();
+                finish();
+            } else {
+                // This activity is part of the application's task, so simply
+                // navigate up to the hierarchical parent activity.
+                NavUtils.navigateUpTo(this, upIntent);
+            }
+            return true;
+    }
+    return super.onOptionsItemSelected(item);
+}
+</pre>
+
+<h2 id="app-home">Properly Handle the Application Home Screen</h2>
+
+<p>By default, the <em>Home</em> button in the action bar is interactive. Since it does not make much sense to navigate home&mdash;or up one level&mdash;while on the home screen, you should disable the button like so:</p>
+
+<pre>
+getActionBar().setHomeButtonEnabled(false);
+</pre>
diff --git a/docs/html/training/implementing-navigation/descendant.jd b/docs/html/training/implementing-navigation/descendant.jd
new file mode 100644
index 0000000..7d0063c
--- /dev/null
+++ b/docs/html/training/implementing-navigation/descendant.jd
@@ -0,0 +1,65 @@
+page.title=Implementing Descendant Navigation
+parent.title=Implementing Effective Navigation
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Implementing Temporal Navigation
+previous.link=temporal.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+  <li><a href="#master-detail">Implement Master/Detail Flows Across Handsets and Tablets</a></li>
+  <li><a href="#external-activities">Navigate into External Activities</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}training/design-navigation/descendant-lateral.html">Providing Descendant and Lateral Navigation</a></li>
+  <li><a href="{@docRoot}design/patterns/app-structure.html">Android Design: App Structure</a></li>
+  <li><a href="{@docRoot}design/patterns/multi-pane-layouts.html">Android Design: Multi-pane Layouts</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/EffectiveNavigation.zip"
+  class="button">Download the sample app</a>
+<p class="filename">EffectiveNavigation.zip</p>
+</div>
+
+</div>
+</div>
+
+
+<p><em>Descendant navigation</em> is navigation down the application's information hierarchy. This is described in <a href="{@docRoot}training/design-navigation/descendant-lateral.html">Designing Effective Navigation</a> and also covered in <a href="{@docRoot}design/patterns/app-structure.html">Android Design: Application Structure</a>.</p>
+
+<p>Descendant navigation is usually implemented using {@link android.content.Intent} objects and {@link android.content.Context#startActivity startActivity()}, or by adding fragments to an activity using {@link android.app.FragmentTransaction} objects. This lesson covers other interesting cases that arise when implementing descendant navigation.</p>
+
+<h2 id="master-detail">Implement Master/Detail Flows Across Handsets and Tablets</h2>
+
+<p>In a <em>master/detail</em> navigation flow, a <em>master</em> screen contains a list of items in a collection, and a <em>detail</em> screen shows detailed information about a specific item within that collection. Implementing navigation from the master screen to the detail screen is one form of descendant navigation.</p>
+
+<p>Handset touchscreens are most suitable for displaying one screen at a time (either the master or the detail screen); this concern is further discussed in <a href="{@docRoot}training/design-navigation/multiple-sizes.html">Planning for Multiple Touchscreen Sizes</a>. Descendant navigation in this case is often implemented using an {@link android.content.Intent} that starts an activity representing the detail screen. On the other hand, tablet displays, especially when viewed in the landscape orientation, are best suited for showing multiple content panes at a time: the master on the left, and the detail to the right). Here, descendant navigation is usually implemented using a {@link android.app.FragmentTransaction} that adds, removes, or replaces the detail pane with new content.</p>
+
+<p>The basics of implementing this pattern are described in the <a href="{@docRoot}training/multiscreen/adaptui.html">Implementing Adaptive UI Flows</a> lesson of the <em>Designing for Multiple Screens</em> class. The class describes how to implement a master/detail flow using two activities on a handset and a single activity on a tablet.</p>
+
+<h2 id="external-activities">Navigate into External Activities</h2>
+
+<p>There are cases where descending into your application's information hierarchy leads to activities from other applications. For example, when viewing the contact details screen for an entry in the phone address book, a child screen detailing recent posts by the contact on a social network may belong to a social networking application.</p>
+
+<p>When launching another application's activity to allow the user to say, compose an email or pick a photo attachment, you generally don't want the user to return to this activity if they relaunch your application from the Launcher (the device home screen). It would be confusing if touching your application icon brought the user to a "compose email" screen.</p>
+
+<p>To prevent this from occurring, simply add the {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET} flag to the intent used to launch the external activity, like so:</p>
+
+<pre>
+Intent externalActivityIntent = new Intent(Intent.ACTION_PICK);
+externalActivityIntent.setType("image/*");
+externalActivityIntent.addFlags(
+        Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+startActivity(externalActivityIntent);
+</pre>
diff --git a/docs/html/training/implementing-navigation/index.jd b/docs/html/training/implementing-navigation/index.jd
new file mode 100644
index 0000000..da61c81
--- /dev/null
+++ b/docs/html/training/implementing-navigation/index.jd
@@ -0,0 +1,68 @@
+page.title=Implementing Effective Navigation
+
+trainingnavtop=true
+startpage=true
+next.title=Implementing Lateral Navigation
+next.link=lateral.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+
+<ul>
+  <li>API level 14</li>
+  <li>Understanding of fragments and Android layouts</li>
+  <li><a href="{@docRoot}sdk/compatibility-library.html">The Android Support Package</a></li>
+  <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a></li>
+</ul>
+
+<h2>You should also read</h2>
+
+<ul>
+  <li><a href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a></li>
+  <li><a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a></li>
+  <li><a href="{@docRoot}training/multiscreen/index.html">Designing for Multiple Screens</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/EffectiveNavigation.zip"
+  class="button">Download the sample app</a>
+<p class="filename">EffectiveNavigation.zip</p>
+</div>
+
+</div>
+</div>
+
+
+<p>This class demonstrates how to implement the key navigation design patterns detailed in the
+<a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a> class.
+The lessons in this class cover implementing navigation up, down, and across your application's <a
+href="{@docRoot}training/design-navigation/screen-planning.html#diagram- relationships">screen
+map</a>.</p>
+
+<p>After reading through the lessons in this class and exploring the associated sample application
+(see right), you should also have a basic understanding of how to use
+{@link android.app.ActionBar} and {@link android.support.v4.view.ViewPager}, two components that are fundamental to core app navigation.</p>
+
+
+<h2 id="lessons">Lessons</h2>
+
+
+<dl>
+  <dt><strong><a href="lateral.html">Implementing Lateral Navigation</a></strong></dt>
+    <dd>Learn how to implement tabs and horizontal paging (swipe views).</dd>
+
+  <dt><strong><a href="ancestral.html">Implementing Ancestral Navigation</a></strong></dt>
+    <dd>Learn how to implement <em>Up</em> navigation.</dd>
+
+  <dt><strong><a href="temporal.html">Implementing Temporal Navigation</a></strong></dt>
+    <dd>Learn how to correctly handle the <em>Back</em> button.</dd>
+
+  <dt><strong><a href="descendant.html">Implementing Descendant Navigation</a></strong></dt>
+    <dd>Learn the finer points of implementing navigation into your application's information hierarchy.</dd>
+</dl>
diff --git a/docs/html/training/implementing-navigation/lateral.jd b/docs/html/training/implementing-navigation/lateral.jd
new file mode 100644
index 0000000..d9ba5c9
--- /dev/null
+++ b/docs/html/training/implementing-navigation/lateral.jd
@@ -0,0 +1,252 @@
+page.title=Implementing Lateral Navigation
+parent.title=Implementing Effective Navigation
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Implementing Ancestral Navigation
+next.link=ancestral.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#tabs">Implement Tabs</a></li>
+  <li><a href="#horizontal-paging">Implement Horizontal Paging (Swipe Views)</a></li>
+  <li><a href="#swipe-tabs">Implement Swiping Between Tabs</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}training/design-navigation/descendant-lateral.html">Providing Descendant and Lateral Navigation</a></li>
+  <li><a href="{@docRoot}design/building-blocks/tabs.html">Android Design: Tabs</a></li>
+  <li><a href="{@docRoot}design/patterns/swipe-views.html">Android Design: Swipe Views</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/EffectiveNavigation.zip"
+  class="button">Download the sample app</a>
+<p class="filename">EffectiveNavigation.zip</p>
+</div>
+
+</div>
+</div>
+
+
+<p><em>Lateral navigation</em> is navigation between sibling screens in the application's screen hierarchy (sometimes referred to as a screen map). The most prominent lateral navigation patterns are tabs and horizontal paging (also known as swipe views). This pattern and others are described in <a href="{@docRoot}training/design-navigation/descendant-lateral.html">Designing Effective Navigation</a>. This lesson covers how to implement several of the primary lateral navigation patterns in Android.</p>
+
+<h2 id="tabs">Implement Tabs</h2>
+
+<p>Tabs allow the user to navigate between sibling screens by selecting the appropriate tab indicator available at the top of the display. In Android 3.0 and later, tabs are implemented using the {@link android.app.ActionBar} class, and are generally set up in {@link android.app.Activity#onCreate Activity.onCreate()}. In some cases, such as when horizontal space is limited and/or the number of tabs is large, an appropriate alternate presentation for tabs is a dropdown list (sometimes implemented using a {@link android.widget.Spinner}).</p>
+
+<p>In previous versions of Android, tabs could be implemented using a {@link android.widget.TabWidget} and {@link android.widget.TabHost}. For details, see the <a href="{@docRoot}resources/tutorials/views/hello-tabwidget.html">Hello, Views</a> tutorial.</p>
+
+<p>As of Android 3.0, however, you should use either {@link android.app.ActionBar#NAVIGATION_MODE_TABS} or {@link android.app.ActionBar#NAVIGATION_MODE_LIST} along with the {@link android.app.ActionBar} class.</p>
+
+<h3>Implement the Tabs Pattern with NAVIGATION_MODE_TABS</h3>
+
+<p>To create tabs, you can use the following code in your activity's {@link android.app.Activity#onCreate onCreate()} method. Note that the exact presentation of tabs may vary per device and by the current device configuration, to make best use of available screen space. For example, Android may automatically collapse tabs into a dropdown list if tabs don't fit horizontally in the action bar.</p>
+
+<pre>
+{@literal @}Override
+public void onCreate(Bundle savedInstanceState) {
+    ...
+    final ActionBar actionBar = getActionBar();
+
+    // Specify that tabs should be displayed in the action bar.
+    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
+    // Create a tab listener that is called when the user changes tabs.
+    ActionBar.TabListener tabListener = new ActionBar.TabListener() {
+        public void onTabSelected(ActionBar.Tab tab,
+                FragmentTransaction ft) { }
+
+        public void onTabUnselected(ActionBar.Tab tab,
+                FragmentTransaction ft) { }
+
+        public void onTabReselected(ActionBar.Tab tab,
+                FragmentTransaction ft) { }
+    };
+
+    // Add 3 tabs.
+    for (int i = 0; i &lt; 3; i++) {
+        actionBar.addTab(
+                actionBar.newTab()
+                        .setText("Tab " + (i + 1))
+                        .setTabListener(tabListener));
+    }
+    ...
+}
+</pre>
+
+<h3>Implement the Tabs Pattern with NAVIGATION_MODE_LIST</h3>
+
+<p>To use a dropdown list instead, use the following code in your activity's {@link android.app.Activity#onCreate onCreate()} method. Dropdown lists are often preferable in cases where more information must be shown per navigation item, such as unread message counts, or where the number of available navigation items is large.</p>
+
+<pre>
+{@literal @}Override
+public void onCreate(Bundle savedInstanceState) {
+    ...
+    final ActionBar actionBar = getActionBar();
+
+    // Specify that a dropdown list should be displayed in the action bar.
+    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+
+    actionBar.setListNavigationCallbacks(
+            // Specify a SpinnerAdapter to populate the dropdown list.
+            new ArrayAdapter<String>(
+                    actionBar.getThemedContext(),
+                    android.R.layout.simple_list_item_1,
+                    android.R.id.text1,
+                    new String[]{ "Tab 1", "Tab 2", "Tab 3" }),
+
+            // Provide a listener to be called when an item is selected.
+            new ActionBar.OnNavigationListener() {
+                public boolean onNavigationItemSelected(
+                        int position, long id) {
+                    // Take action here, e.g. switching to the
+                    // corresponding fragment.
+                    return true;
+                }
+            });
+    ...
+}
+</pre>
+
+<h2 id="horizontal-paging">Implement Horizontal Paging (Swipe Views)</h2>
+
+<p>Horizontal paging, or swipe views, allow users to <a href="{@docRoot}design/patterns/swipe-views">swipe</a> horizontally on the current screen to navigate to adjacent screens. This pattern can be implemented using the {@link android.support.v4.view.ViewPager} widget, currently available as part of the <a href="{@docRoot}sdk/compatibility-library.html">Android Support Package</a>. For navigating between sibling screens representing a fixed number of sections, it's best to provide the {@link android.support.v4.view.ViewPager} with a {@link android.support.v4.app.FragmentPagerAdapter}. For horizontal paging across collections of objects, it's best to use a {@link android.support.v4.app.FragmentStatePagerAdapter}, which destroys fragments as the user navigates to other pages, minimizing memory usage.</p>
+
+<p>Below is an example of using a {@link android.support.v4.view.ViewPager} to swipe across a collection of objects.</p>
+
+<pre>
+public class CollectionDemoActivity extends FragmentActivity {
+    // When requested, this adapter returns a DemoObjectFragment,
+    // representing an object in the collection.
+    DemoCollectionPagerAdapter mDemoCollectionPagerAdapter;
+    ViewPager mViewPager;
+
+    public void onCreate(Bundle savedInstanceState) {
+        // ViewPager and its adapters use support library
+        // fragments, so use getSupportFragmentManager.
+        mDemoCollectionPagerAdapter =
+                new DemoCollectionPagerAdapter(
+                        getSupportFragmentManager());
+        mViewPager = (ViewPager) findViewById(R.id.pager);
+        mViewPager.setAdapter(mDemoCollectionPagerAdapter);
+    }
+}
+
+// Since this is an object collection, use a FragmentStatePagerAdapter,
+// and NOT a FragmentPagerAdapter.
+public class DemoCollectionPagerAdapter extends
+        FragmentStatePagerAdapter {
+    public DemoCollectionPagerAdapter(FragmentManager fm) {
+        super(fm);
+    }
+
+    {@literal @}Override
+    public Fragment getItem(int i) {
+        Fragment fragment = new DemoObjectFragment();
+        Bundle args = new Bundle();
+        // Our object is just an integer :-P
+        args.putInt(DemoObjectFragment.ARG_OBJECT, i + 1);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    {@literal @}Override
+    public int getCount() {
+        return 100;
+    }
+
+    {@literal @}Override
+    public CharSequence getPageTitle(int position) {
+        return "OBJECT " + (position + 1);
+    }
+}
+
+// Instances of this class are fragments representing a single
+// object in our collection.
+public static class DemoObjectFragment extends Fragment {
+    public static final String ARG_OBJECT = "object";
+
+    {@literal @}Override
+    public View onCreateView(LayoutInflater inflater,
+            ViewGroup container, Bundle savedInstanceState) {
+        // The last two arguments ensure LayoutParams are inflated
+        // properly.
+        View rootView = inflater.inflate(
+                R.layout.fragment_collection_object, container, false);
+        Bundle args = getArguments();
+        ((TextView) rootView.findViewById(android.R.id.text1)).setText(
+                Integer.toString(args.getInt(ARG_OBJECT)));
+        return rootView;
+    }
+}
+</pre>
+
+<p>You can also add indicators to your horizontal paging UI by adding a {@link android.support.v4.view.PagerTitleStrip}. Below is an example layout XML file for an activity whose entire contents are a {@link android.support.v4.view.ViewPager} and a top-aligned {@link android.support.v4.view.PagerTitleStrip} inside it. Individual pages (provided by the adapter) occupy the remaining space inside the {@link android.support.v4.view.ViewPager}.</p>
+
+<pre>
+&lt;android.support.v4.view.ViewPager
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/pager"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"&gt;
+
+    &lt;android.support.v4.view.PagerTitleStrip
+        android:id="@+id/pager_title_strip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:background="#33b5e5"
+        android:textColor="#fff"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp" /&gt;
+
+&lt;/android.support.v4.view.ViewPager&gt;
+</pre>
+
+<h2 id="swipe-tabs">Implement Swiping Between Tabs</h2>
+
+<p>One of the key design recommendations in Android 4.0 for tabs is to <a href="{@docRoot}design/patterns/swipe-views.html">allow swiping</a> between them where appropriate. This behavior enables users to swipe horizontally across the selected tab's contents to navigate to adjacent tabs, without needed to directly interact with the tabs themselves. To implement this, you can use a {@link android.support.v4.view.ViewPager} in conjunction with the {@link android.app.ActionBar} tabs API.</p>
+
+<p>Upon observing the current page changing, select the corresponding tab. You can set up this behavior using an {@link android.support.v4.view.ViewPager.OnPageChangeListener} in your activity's {@link android.app.Activity#onCreate onCreate()} method:</p>
+
+<pre>
+{@literal @}Override
+public void onCreate(Bundle savedInstanceState) {
+    ...
+    mViewPager.setOnPageChangeListener(
+            new ViewPager.SimpleOnPageChangeListener() {
+                {@literal @}Override
+                public void onPageSelected(int position) {
+                    // When swiping between pages, select the
+                    // corresponding tab.
+                    getActionBar().setSelectedNavigationItem(position);
+                }
+            });
+    ...
+}
+</pre>
+
+<p>And upon selecting a tab, switch to the corresponding page in the {@link android.support.v4.view.ViewPager}. To do this, add an {@link android.app.ActionBar.TabListener} to your tab when creating it using the {@link android.app.ActionBar#newTab newTab()} method:</p>
+
+<pre>
+actionBar.newTab()
+        ...
+        .setTabListener(new ActionBar.TabListener() {
+            public void onTabSelected(ActionBar.Tab tab,
+                    FragmentTransaction ft) {
+                // When the tab is selected, switch to the
+                // corresponding page in the ViewPager.
+                mViewPager.setCurrentItem(tab.getPosition());
+            }
+            ...
+        }));
+</pre>
diff --git a/docs/html/training/implementing-navigation/temporal.jd b/docs/html/training/implementing-navigation/temporal.jd
new file mode 100644
index 0000000..f36991f
--- /dev/null
+++ b/docs/html/training/implementing-navigation/temporal.jd
@@ -0,0 +1,83 @@
+page.title=Implementing Temporal Navigation
+parent.title=Implementing Effective Navigation
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Implementing Ancestral Navigation
+previous.link=ancestral.html
+next.title=Implementing Descendant Navigation
+next.link=descendant.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+  <li><a href="#back-fragments">Implement <em>Back</em> Navigation with Fragments</a></li>
+  <li><a href="#back-webviews">Implement <em>Back</em> Navigation with WebViews</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}training/design-navigation/ancestral-temporal.html">Providing Ancestral and Temporal Navigation</a></li>
+  <li><a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a></li>
+  <li><a href="{@docRoot}design/patterns/navigation.html">Android Design: Navigation</a></li>
+</ul>
+
+</div>
+</div>
+
+
+<p><em>Temporal navigation</em> is navigation to previously visited screens. Users can visit previous screens by pressing the device <em>Back</em> button. This user interface pattern is described further in <a href="{@docRoot}training/design-navigation/ancestral-temporal.html">Providing Ancestral and Temporal Navigation</a> in <em>Designing Effective Navigation</em> and in <a href="{@docRoot}design/patterns/navigation.html">Android Design: Navigation</a>.</p>
+
+<p>Android handles basic <em>Back</em> navigation for you (see <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a> for details on this behavior). This lesson discusses a number of cases where applications should provide specialized logic for the <em>Back</em> button.</p>
+
+
+<h2 id="back-fragments">Implement <em>Back</em> Navigation with Fragments</h2>
+
+<p>When using fragments in your application, individual {@link android.app.FragmentTransaction} objects can represent context changes that should be added to the back stack. For example, if you are implementing a <a href="descendant.html#master-detail">master/detail flow</a> on a handset by swapping out fragments (thus emulating a {@link android.app.Activity#startActivity startActivity()} call), you should ensure that pressing the <em>Back</em> button on a detail screen returns the user to the master screen. To do so, you can use {@link android.app.FragmentTransaction#addToBackStack addToBackStack()}:</p>
+
+<pre>
+// Works with either the framework FragmentManager or the
+// support package FragmentManager (getSupportFragmentManager).
+getFragmentManager().beginTransaction()
+        .add(detailFragment, "detail")
+
+        // Add this transaction to the back stack and commit.
+        .addToBackStack()
+        .commit();
+</pre>
+
+<p>The activity's {@link android.app.FragmentManager} handles <em>Back</em> button presses if there are {@link android.app.FragmentTransaction} objects on the back stack. When this happens, the {@link android.app.FragmentManager} pops the most recent transaction off the back stack and performs the reverse action (e.g., removing a fragment if the transaction added it).</p>
+
+<p>If your application updates other user interface elements to reflect the current state of your fragments, such as the action bar, remember to update the UI when you commit the transaction. You should update your user interface after the fragment manager back stack changes in addition to when you commit the transaction. You can listen for when a <code>FragmentTransaction</code> is reverted by setting up an {@link android.app.FragmentManager.OnBackStackChangedListener}:</p>
+
+<pre>
+getFragmentManager().addOnBackStackChangedListener(
+        new FragmentManager.OnBackStackChangedListener() {
+            public void onBackStackChanged() {
+                // Update your UI here.
+            }
+        });
+</pre>
+
+<h2 id="back-webviews">Implement <em>Back</em> Navigation with WebViews</h2>
+
+<p>If a part of your application is contained in a {@link android.webkit.WebView}, it may be appropriate for <em>Back</em> to traverse browser history. To do so, you can override {@link android.app.Activity#onBackPressed onBackPressed()} and proxy to the <code>WebView</code> if it has history state:</p>
+
+<pre>
+{@literal @}Override
+public void onBackPressed() {
+    if (mWebView.canGoBack()) {
+        mWebView.goBack();
+        return;
+    }
+
+    // Otherwise defer to system default behavior.
+    super.onBackPressed();
+}
+</pre>
+
+<p>Be careful when using this mechanism with highly dynamic web pages that can grow a large history. Pages that generate an extensive history, such as those that make frequent changes to the document hash, may make it tedious for users to get out of your activity.</p>
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index e101581..3fc20b5 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -155,19 +155,23 @@
 
     /**
      * Update the texture image to the most recent frame from the image stream.  This may only be
-     * called while the OpenGL ES context that owns the texture is bound to the thread.  It will
-     * implicitly bind its texture to the GL_TEXTURE_EXTERNAL_OES texture target.
+     * called while the OpenGL ES context that owns the texture is current on the calling thread.
+     * It will implicitly bind its texture to the GL_TEXTURE_EXTERNAL_OES texture target.
      */
     public void updateTexImage() {
-        int err = nativeUpdateTexImage(); 
-        if (err != 0) {
-            throw new RuntimeException("Error during updateTexImage (see logcat for details)");
-        }
+        nativeUpdateTexImage(); 
     }
 
     /**
-     * Detach the SurfaceTexture from the OpenGL ES context with which it is currently associated.
-     * This can be used to change from one OpenGL ES context to another.
+     * Detach the SurfaceTexture from the OpenGL ES context that owns the OpenGL ES texture object.
+     * This call must be made with the OpenGL ES context current on the calling thread.  The OpenGL
+     * ES texture object will be deleted as a result of this call.  After calling this method all
+     * calls to {@link #updateTexImage} will throw an {@link java.lang.IllegalStateException} until
+     * a successful call to {@link #attachToGLContext} is made.
+     *
+     * This can be used to access the SurfaceTexture image contents from multiple OpenGL ES
+     * contexts.  Note, however, that the image contents are only accessible from one OpenGL ES
+     * context at a time.
      *
      * @hide
      */
@@ -179,6 +183,17 @@
     }
 
     /**
+     * Attach the SurfaceTexture to the OpenGL ES context that is current on the calling thread.  A
+     * new OpenGL ES texture object is created and populated with the SurfaceTexture image frame
+     * that was current at the time of the last call to {@link #detachFromGLContext}.  This new
+     * texture is bound to the GL_TEXTURE_EXTERNAL_OES texture target.
+     *
+     * This can be used to access the SurfaceTexture image contents from multiple OpenGL ES
+     * contexts.  Note, however, that the image contents are only accessible from one OpenGL ES
+     * context at a time.
+     *
+     * @param texName The name of the OpenGL ES texture that will be created.  This texture name
+     * must be unusued in the OpenGL ES context that is current on the calling thread.
      *
      * @hide
      */
@@ -292,7 +307,7 @@
     private native void nativeGetTransformMatrix(float[] mtx);
     private native long nativeGetTimestamp();
     private native void nativeSetDefaultBufferSize(int width, int height);
-    private native int nativeUpdateTexImage();
+    private native void nativeUpdateTexImage();
     private native int nativeDetachFromGLContext();
     private native int nativeAttachToGLContext(int texName);
     private native int nativeGetQueuedCount();
diff --git a/include/androidfw/InputDevice.h b/include/androidfw/InputDevice.h
index 2eac544..38203af 100644
--- a/include/androidfw/InputDevice.h
+++ b/include/androidfw/InputDevice.h
@@ -93,6 +93,9 @@
         return mKeyCharacterMap;
     }
 
+    inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; }
+    inline bool hasVibrator() const { return mHasVibrator; }
+
     inline const Vector<MotionRange>& getMotionRanges() const {
         return mMotionRanges;
     }
@@ -105,6 +108,7 @@
     uint32_t mSources;
     int32_t mKeyboardType;
     sp<KeyCharacterMap> mKeyCharacterMap;
+    bool mHasVibrator;
 
     Vector<MotionRange> mMotionRanges;
 };
diff --git a/libs/androidfw/InputDevice.cpp b/libs/androidfw/InputDevice.cpp
index 6bb06a9..d6c49f7 100644
--- a/libs/androidfw/InputDevice.cpp
+++ b/libs/androidfw/InputDevice.cpp
@@ -136,6 +136,7 @@
         mSources(other.mSources),
         mKeyboardType(other.mKeyboardType),
         mKeyCharacterMap(other.mKeyCharacterMap),
+        mHasVibrator(other.mHasVibrator),
         mMotionRanges(other.mMotionRanges) {
 }
 
@@ -150,6 +151,7 @@
     mDescriptor = descriptor;
     mSources = 0;
     mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
+    mHasVibrator = false;
     mMotionRanges.clear();
 }
 
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 56826fa..1654eca 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -48,8 +48,7 @@
     <string name="status_bar_settings_notifications" msgid="397146176280905137">"Kennisgewings"</string>
     <string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth-verbind"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Stel invoer metodes op"</string>
-    <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
-    <skip />
+    <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fisiese sleutelbord"</string>
     <string name="usb_device_permission_prompt" msgid="834698001271562057">"Laat die program <xliff:g id="APPLICATION">%1$s</xliff:g> toe om toegang tot die USB-toestel te kry?"</string>
     <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Laat die program <xliff:g id="APPLICATION">%1$s</xliff:g> toe om toegang tot die USB-toebehoorsel te kry?"</string>
     <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Maak <xliff:g id="ACTIVITY">%1$s</xliff:g> oop wanneer hierdie USB-toestel gekoppel is?"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index b885503..433c124 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -48,8 +48,7 @@
     <string name="status_bar_settings_notifications" msgid="397146176280905137">"Паведамленні"</string>
     <string name="bluetooth_tethered" msgid="7094101612161133267">"Прывязаныя праз Bluetooth"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Налада метадаў уводу"</string>
-    <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
-    <skip />
+    <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Фізічная клавіятура"</string>
     <string name="usb_device_permission_prompt" msgid="834698001271562057">"Дазволіць праыкладанню <xliff:g id="APPLICATION">%1$s</xliff:g> атрымлiваць доступ да прылады USB?"</string>
     <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Дазволіць прыкладанню <xliff:g id="APPLICATION">%1$s</xliff:g> доступ да прылады USB?"</string>
     <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Адкрыць <xliff:g id="ACTIVITY">%1$s</xliff:g>, калі гэтая USB-прылада падлучаная?"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index ac58b06..6c614cd 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -48,8 +48,7 @@
     <string name="status_bar_settings_notifications" msgid="397146176280905137">"Underretninger"</string>
     <string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth-tethering anvendt"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfigurer inputmetoder"</string>
-    <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
-    <skip />
+    <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fysisk tastatur"</string>
     <string name="usb_device_permission_prompt" msgid="834698001271562057">"Tillad, at appen <xliff:g id="APPLICATION">%1$s</xliff:g> kan få adgang til USB-enheden?"</string>
     <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Vil du tillade, at appen <xliff:g id="APPLICATION">%1$s</xliff:g> får adgang til USB-enheden?"</string>
     <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Vil du åbne <xliff:g id="ACTIVITY">%1$s</xliff:g>, når denne USB-enhed er tilsluttet?"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 148924a..b20a5f3 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -48,8 +48,7 @@
     <string name="status_bar_settings_notifications" msgid="397146176280905137">"Notifications"</string>
     <string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth tethered"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Set up input methods"</string>
-    <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
-    <skip />
+    <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physical keyboard"</string>
     <string name="usb_device_permission_prompt" msgid="834698001271562057">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB device?"</string>
     <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB accessory?"</string>
     <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB device is connected?"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 8e3a1e3..055e3ee 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -48,8 +48,7 @@
     <string name="status_bar_settings_notifications" msgid="397146176280905137">"Teatised"</string>
     <string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth on jagatud"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Seadista sisestusmeetodeid"</string>
-    <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
-    <skip />
+    <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Füüsiline klaviatuur"</string>
     <string name="usb_device_permission_prompt" msgid="834698001271562057">"Kas lubate rakendusel <xliff:g id="APPLICATION">%1$s</xliff:g> USB-seadmele juurde pääseda?"</string>
     <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Kas lubate rakendusel <xliff:g id="APPLICATION">%1$s</xliff:g> USB-seadmele juurde pääseda?"</string>
     <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Kas avada <xliff:g id="ACTIVITY">%1$s</xliff:g>, kui see USB-seade on ühendatud?"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 1a8c6a7..c7f307c 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -48,8 +48,7 @@
     <string name="status_bar_settings_notifications" msgid="397146176280905137">"Notifications"</string>
     <string name="bluetooth_tethered" msgid="7094101612161133267">"Connexion Bluetooth partagée"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurer les modes de saisie"</string>
-    <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
-    <skip />
+    <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Clavier physique"</string>
     <string name="usb_device_permission_prompt" msgid="834698001271562057">"Autoriser l\'application <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder au périphérique USB ?"</string>
     <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Autoriser l\'application <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à l\'accessoire USB ?"</string>
     <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Ouvrir <xliff:g id="ACTIVITY">%1$s</xliff:g> lors de la connexion de ce périphérique USB ?"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index cdf7c9d..a725377 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -48,8 +48,7 @@
     <string name="status_bar_settings_notifications" msgid="397146176280905137">"Notifiche"</string>
     <string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth con tethering"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configura metodi di immissione"</string>
-    <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
-    <skip />
+    <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Tastiera fisica"</string>
     <string name="usb_device_permission_prompt" msgid="834698001271562057">"Consentire all\'applicazione <xliff:g id="APPLICATION">%1$s</xliff:g> di accedere al dispositivo USB?"</string>
     <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Consentire all\'applicazione <xliff:g id="APPLICATION">%1$s</xliff:g> di accedere all\'accessorio USB?"</string>
     <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Aprire <xliff:g id="ACTIVITY">%1$s</xliff:g> quando questo dispositivo USB è collegato?"</string>
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 0a63840..897b8d0 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -909,7 +909,7 @@
             mPluggedIn = (0 != intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0));
         }
 
-        mVibrator = new Vibrator();
+        mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
         mLongPressVibePattern = getLongIntArray(mContext.getResources(),
                 com.android.internal.R.array.config_longPressVibePattern);
         mVirtualKeyVibePattern = getLongIntArray(mContext.getResources(),
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index fbffc94..c0eb1b9 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -161,12 +161,14 @@
         const InputDeviceIdentifier& identifier) :
         next(NULL),
         fd(fd), id(id), path(path), identifier(identifier),
-        classes(0), configuration(NULL), virtualKeyMap(NULL) {
+        classes(0), configuration(NULL), virtualKeyMap(NULL),
+        ffEffectPlaying(false), ffEffectId(-1) {
     memset(keyBitmask, 0, sizeof(keyBitmask));
     memset(absBitmask, 0, sizeof(absBitmask));
     memset(relBitmask, 0, sizeof(relBitmask));
     memset(swBitmask, 0, sizeof(swBitmask));
     memset(ledBitmask, 0, sizeof(ledBitmask));
+    memset(ffBitmask, 0, sizeof(ffBitmask));
     memset(propBitmask, 0, sizeof(propBitmask));
 }
 
@@ -534,6 +536,62 @@
     return NULL;
 }
 
+void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
+    AutoMutex _l(mLock);
+    Device* device = getDeviceLocked(deviceId);
+    if (device && !device->isVirtual()) {
+        ff_effect effect;
+        memset(&effect, 0, sizeof(effect));
+        effect.type = FF_RUMBLE;
+        effect.id = device->ffEffectId;
+        effect.u.rumble.strong_magnitude = 0xc000;
+        effect.u.rumble.weak_magnitude = 0xc000;
+        effect.replay.length = (duration + 999999LL) / 1000000LL;
+        effect.replay.delay = 0;
+        if (ioctl(device->fd, EVIOCSFF, &effect)) {
+            ALOGW("Could not upload force feedback effect to device %s due to error %d.",
+                    device->identifier.name.string(), errno);
+            return;
+        }
+        device->ffEffectId = effect.id;
+
+        struct input_event ev;
+        ev.time.tv_sec = 0;
+        ev.time.tv_usec = 0;
+        ev.type = EV_FF;
+        ev.code = device->ffEffectId;
+        ev.value = 1;
+        if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
+            ALOGW("Could not start force feedback effect on device %s due to error %d.",
+                    device->identifier.name.string(), errno);
+            return;
+        }
+        device->ffEffectPlaying = true;
+    }
+}
+
+void EventHub::cancelVibrate(int32_t deviceId) {
+    AutoMutex _l(mLock);
+    Device* device = getDeviceLocked(deviceId);
+    if (device && !device->isVirtual()) {
+        if (device->ffEffectPlaying) {
+            device->ffEffectPlaying = false;
+
+            struct input_event ev;
+            ev.time.tv_sec = 0;
+            ev.time.tv_usec = 0;
+            ev.type = EV_FF;
+            ev.code = device->ffEffectId;
+            ev.value = 0;
+            if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
+                ALOGW("Could not stop force feedback effect on device %s due to error %d.",
+                        device->identifier.name.string(), errno);
+                return;
+            }
+        }
+    }
+}
+
 EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
     if (deviceId == BUILT_IN_KEYBOARD_ID) {
         deviceId = mBuiltInKeyboardId;
@@ -949,6 +1007,7 @@
     ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
     ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
     ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
+    ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
     ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
 
     // See if this is a keyboard.  Ignore everything in the button range except for
@@ -1010,6 +1069,11 @@
         }
     }
 
+    // Check whether this device supports the vibrator.
+    if (test_bit(FF_RUMBLE, device->ffBitmask)) {
+        device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
+    }
+
     // Configure virtual keys.
     if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
         // Load the virtual keys for the touch screen, if any.
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 88159e7..51d2bac 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -113,6 +113,9 @@
     /* The input device is a joystick (implies gamepad, has joystick absolute axes). */
     INPUT_DEVICE_CLASS_JOYSTICK      = 0x00000100,
 
+    /* The input device has a vibrator (supports FF_RUMBLE). */
+    INPUT_DEVICE_CLASS_VIBRATOR      = 0x00000200,
+
     /* The input device is virtual (not a real device, not part of UI configuration). */
     INPUT_DEVICE_CLASS_VIRTUAL       = 0x40000000,
 
@@ -219,6 +222,10 @@
 
     virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
 
+    /* Control the vibrator. */
+    virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
+    virtual void cancelVibrate(int32_t deviceId) = 0;
+
     /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
     virtual void requestReopenDevices() = 0;
 
@@ -277,6 +284,9 @@
 
     virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
 
+    virtual void vibrate(int32_t deviceId, nsecs_t duration);
+    virtual void cancelVibrate(int32_t deviceId);
+
     virtual void requestReopenDevices();
 
     virtual void wake();
@@ -303,6 +313,7 @@
         uint8_t relBitmask[(REL_MAX + 1) / 8];
         uint8_t swBitmask[(SW_MAX + 1) / 8];
         uint8_t ledBitmask[(LED_MAX + 1) / 8];
+        uint8_t ffBitmask[(FF_MAX + 1) / 8];
         uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
 
         String8 configurationFile;
@@ -310,6 +321,9 @@
         VirtualKeyMap* virtualKeyMap;
         KeyMap keyMap;
 
+        bool ffEffectPlaying;
+        int16_t ffEffectId; // initially -1
+
         Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
         ~Device();
 
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 71eba52..8c37fbb 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -36,6 +36,9 @@
 // Log debug messages about gesture detection.
 #define DEBUG_GESTURES 0
 
+// Log debug messages about the vibrator.
+#define DEBUG_VIBRATOR 0
+
 #include "InputReader.h"
 
 #include <cutils/log.h>
@@ -273,9 +276,7 @@
             mConfigurationChangesToRefresh = 0;
             timeoutMillis = 0;
             refreshConfigurationLocked(changes);
-        }
-
-        if (timeoutMillis < 0 && mNextTimeout != LLONG_MAX) {
+        } else if (mNextTimeout != LLONG_MAX) {
             nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
             timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
         }
@@ -426,6 +427,11 @@
         device->addMapper(new SwitchInputMapper(device));
     }
 
+    // Vibrator-like devices.
+    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
+        device->addMapper(new VibratorInputMapper(device));
+    }
+
     // Keyboard-like devices.
     uint32_t keyboardSource = 0;
     int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
@@ -594,6 +600,7 @@
 void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) {
     if (when < mNextTimeout) {
         mNextTimeout = when;
+        mEventHub->wake();
     }
 }
 
@@ -721,6 +728,27 @@
     }
 }
 
+void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+        ssize_t repeat, int32_t token) {
+    AutoMutex _l(mLock);
+
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex >= 0) {
+        InputDevice* device = mDevices.valueAt(deviceIndex);
+        device->vibrate(pattern, patternSize, repeat, token);
+    }
+}
+
+void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
+    AutoMutex _l(mLock);
+
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex >= 0) {
+        InputDevice* device = mDevices.valueAt(deviceIndex);
+        device->cancelVibrate(token);
+    }
+}
+
 void InputReader::dump(String8& dump) {
     AutoMutex _l(mLock);
 
@@ -1054,6 +1082,23 @@
     return result;
 }
 
+void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+        int32_t token) {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->vibrate(pattern, patternSize, repeat, token);
+    }
+}
+
+void InputDevice::cancelVibrate(int32_t token) {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->cancelVibrate(token);
+    }
+}
+
 int32_t InputDevice::getMetaState() {
     int32_t result = 0;
     size_t numMappers = mMappers.size();
@@ -1739,6 +1784,13 @@
     return false;
 }
 
+void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+        int32_t token) {
+}
+
+void InputMapper::cancelVibrate(int32_t token) {
+}
+
 int32_t InputMapper::getMetaState() {
     return 0;
 }
@@ -1796,6 +1848,120 @@
 }
 
 
+// --- VibratorInputMapper ---
+
+VibratorInputMapper::VibratorInputMapper(InputDevice* device) :
+        InputMapper(device), mVibrating(false) {
+}
+
+VibratorInputMapper::~VibratorInputMapper() {
+}
+
+uint32_t VibratorInputMapper::getSources() {
+    return 0;
+}
+
+void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    info->setVibrator(true);
+}
+
+void VibratorInputMapper::process(const RawEvent* rawEvent) {
+    // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
+}
+
+void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+        int32_t token) {
+#if DEBUG_VIBRATOR
+    String8 patternStr;
+    for (size_t i = 0; i < patternSize; i++) {
+        if (i != 0) {
+            patternStr.append(", ");
+        }
+        patternStr.appendFormat("%lld", pattern[i]);
+    }
+    ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%ld, token=%d",
+            getDeviceId(), patternStr.string(), repeat, token);
+#endif
+
+    mVibrating = true;
+    memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
+    mPatternSize = patternSize;
+    mRepeat = repeat;
+    mToken = token;
+    mIndex = -1;
+
+    nextStep();
+}
+
+void VibratorInputMapper::cancelVibrate(int32_t token) {
+#if DEBUG_VIBRATOR
+    ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
+#endif
+
+    if (mVibrating && mToken == token) {
+        stopVibrating();
+    }
+}
+
+void VibratorInputMapper::timeoutExpired(nsecs_t when) {
+    if (mVibrating) {
+        if (when >= mNextStepTime) {
+            nextStep();
+        } else {
+            getContext()->requestTimeoutAtTime(mNextStepTime);
+        }
+    }
+}
+
+void VibratorInputMapper::nextStep() {
+    mIndex += 1;
+    if (size_t(mIndex) >= mPatternSize) {
+        if (mRepeat < 0) {
+            // We are done.
+            stopVibrating();
+            return;
+        }
+        mIndex = mRepeat;
+    }
+
+    bool vibratorOn = mIndex & 1;
+    nsecs_t duration = mPattern[mIndex];
+    if (vibratorOn) {
+#if DEBUG_VIBRATOR
+        ALOGD("nextStep: sending vibrate deviceId=%d, duration=%lld",
+                getDeviceId(), duration);
+#endif
+        getEventHub()->vibrate(getDeviceId(), duration);
+    } else {
+#if DEBUG_VIBRATOR
+        ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
+#endif
+        getEventHub()->cancelVibrate(getDeviceId());
+    }
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    mNextStepTime = now + duration;
+    getContext()->requestTimeoutAtTime(mNextStepTime);
+#if DEBUG_VIBRATOR
+    ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
+#endif
+}
+
+void VibratorInputMapper::stopVibrating() {
+    mVibrating = false;
+#if DEBUG_VIBRATOR
+    ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
+#endif
+    getEventHub()->cancelVibrate(getDeviceId());
+}
+
+void VibratorInputMapper::dump(String8& dump) {
+    dump.append(INDENT2 "Vibrator Input Mapper:\n");
+    dump.appendFormat(INDENT3 "Vibrating: %s\n", toString(mVibrating));
+}
+
+
 // --- KeyboardInputMapper ---
 
 KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index d29776d8..ed57596 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -33,6 +33,14 @@
 #include <stddef.h>
 #include <unistd.h>
 
+// Maximum supported size of a vibration pattern.
+// Must be at least 2.
+#define MAX_VIBRATE_PATTERN_SIZE 100
+
+// Maximum allowable delay value in a vibration pattern before
+// which the delay will be truncated.
+#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
+
 namespace android {
 
 class InputDevice;
@@ -267,6 +275,11 @@
      * The changes flag is a bitfield that indicates what has changed and whether
      * the input devices must all be reopened. */
     virtual void requestRefreshConfiguration(uint32_t changes) = 0;
+
+    /* Controls the vibrator of a particular input device. */
+    virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+            ssize_t repeat, int32_t token) = 0;
+    virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
 };
 
 
@@ -334,6 +347,10 @@
 
     virtual void requestRefreshConfiguration(uint32_t changes);
 
+    virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+            ssize_t repeat, int32_t token);
+    virtual void cancelVibrate(int32_t deviceId, int32_t token);
+
 protected:
     // These members are protected so they can be instrumented by test cases.
     virtual InputDevice* createDeviceLocked(int32_t deviceId,
@@ -466,6 +483,8 @@
     int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
     bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags);
+    void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+    void cancelVibrate(int32_t token);
 
     int32_t getMetaState();
 
@@ -848,6 +867,9 @@
     virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
     virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags);
+    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+            int32_t token);
+    virtual void cancelVibrate(int32_t token);
 
     virtual int32_t getMetaState();
 
@@ -880,6 +902,35 @@
 };
 
 
+class VibratorInputMapper : public InputMapper {
+public:
+    VibratorInputMapper(InputDevice* device);
+    virtual ~VibratorInputMapper();
+
+    virtual uint32_t getSources();
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void process(const RawEvent* rawEvent);
+
+    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+            int32_t token);
+    virtual void cancelVibrate(int32_t token);
+    virtual void timeoutExpired(nsecs_t when);
+    virtual void dump(String8& dump);
+
+private:
+    bool mVibrating;
+    nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
+    size_t mPatternSize;
+    ssize_t mRepeat;
+    int32_t mToken;
+    ssize_t mIndex;
+    nsecs_t mNextStepTime;
+
+    void nextStep();
+    void stopVibrating();
+};
+
+
 class KeyboardInputMapper : public InputMapper {
 public:
     KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index e59af4e..94d4189 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -646,6 +646,12 @@
         return NULL;
     }
 
+    virtual void vibrate(int32_t deviceId, nsecs_t duration) {
+    }
+
+    virtual void cancelVibrate(int32_t deviceId) {
+    }
+
     virtual bool isExternal(int32_t deviceId) const {
         return false;
     }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 3db4601..b22be76 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -100,7 +100,7 @@
     private int mDisabledNotifications;
 
     private NotificationRecord mVibrateNotification;
-    private Vibrator mVibrator = new Vibrator();
+    private Vibrator mVibrator;
 
     // for enabling and disabling notification pulse behavior
     private boolean mScreenOn = true;
@@ -398,6 +398,7 @@
     {
         super();
         mContext = context;
+        mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
         mAm = ActivityManagerNative.getDefault();
         mSound = new NotificationPlayer(TAG);
         mSound.setUsesWakeLock(context);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 241d04ff..75dcf8c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -115,6 +115,7 @@
         LightsService lights = null;
         PowerManagerService power = null;
         BatteryService battery = null;
+        VibratorService vibrator = null;
         AlarmManagerService alarm = null;
         NetworkManagementService networkManagement = null;
         NetworkStatsService networkStats = null;
@@ -203,7 +204,8 @@
             ServiceManager.addService("battery", battery);
 
             Slog.i(TAG, "Vibrator Service");
-            ServiceManager.addService("vibrator", new VibratorService(context));
+            vibrator = new VibratorService(context);
+            ServiceManager.addService("vibrator", vibrator);
 
             // only initialize the power service after we have started the
             // lights service, content providers and the battery service.
@@ -645,6 +647,12 @@
 
         // It is now time to start up the app processes...
 
+        try {
+            vibrator.systemReady();
+        } catch (Throwable e) {
+            reportWtf("making Vibrator Service ready", e);
+        }
+
         if (devicePolicy != null) {
             try {
                 devicePolicy.systemReady();
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index de25747..7561b0b 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -21,6 +21,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.hardware.input.InputManager;
 import android.os.Handler;
 import android.os.IVibratorService;
 import android.os.PowerManager;
@@ -29,18 +31,41 @@
 import android.os.IBinder;
 import android.os.Binder;
 import android.os.SystemClock;
+import android.os.Vibrator;
 import android.os.WorkSource;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.util.Slog;
+import android.view.InputDevice;
 
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.ListIterator;
 
-public class VibratorService extends IVibratorService.Stub {
+public class VibratorService extends IVibratorService.Stub
+        implements InputManager.InputDeviceListener {
     private static final String TAG = "VibratorService";
 
     private final LinkedList<Vibration> mVibrations;
     private Vibration mCurrentVibration;
     private final WorkSource mTmpWorkSource = new WorkSource();
+    private final Handler mH = new Handler();
+
+    private final Context mContext;
+    private final PowerManager.WakeLock mWakeLock;
+    private InputManager mIm;
+
+    volatile VibrateThread mThread;
+
+    // mInputDeviceVibrators lock should be acquired after mVibrations lock, if both are
+    // to be acquired
+    private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
+    private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
+    private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
+
+    native static boolean vibratorExists();
+    native static void vibratorOn(long milliseconds);
+    native static void vibratorOff();
 
     private class Vibration implements IBinder.DeathRecipient {
         private final IBinder mToken;
@@ -112,10 +137,23 @@
         context.registerReceiver(mIntentReceiver, filter);
     }
 
-    public boolean hasVibrator() {
-        return vibratorExists();
+    public void systemReady() {
+        mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE);
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), true,
+                new ContentObserver(mH) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        updateVibrateInputDevicesSetting();
+                    }
+                });
+        updateVibrateInputDevicesSetting();
     }
-    
+
+    public boolean hasVibrator() {
+        return doVibratorExists();
+    }
+
     public void vibrate(long milliseconds, IBinder token) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -131,6 +169,7 @@
             // longer than milliseconds.
             return;
         }
+
         Vibration vib = new Vibration(token, milliseconds, uid);
         synchronized (mVibrations) {
             removeVibrationLocked(token);
@@ -240,7 +279,7 @@
             }
             mThread = null;
         }
-        vibratorOff();
+        doVibratorOff();
         mH.removeCallbacks(mVibrationRunnable);
     }
 
@@ -257,7 +296,7 @@
     // Lock held on mVibrations
     private void startVibrationLocked(final Vibration vib) {
         if (vib.mTimeout != 0) {
-            vibratorOn(vib.mTimeout);
+            doVibratorOn(vib.mTimeout);
             mH.postDelayed(mVibrationRunnable, vib.mTimeout);
         } else {
             // mThread better be null here. doCancelVibrate should always be
@@ -295,6 +334,100 @@
         }
     }
 
+    private void updateVibrateInputDevicesSetting() {
+        synchronized (mInputDeviceVibrators) {
+            mVibrateInputDevicesSetting = false;
+            try {
+                mVibrateInputDevicesSetting = Settings.System.getInt(mContext.getContentResolver(),
+                        Settings.System.VIBRATE_INPUT_DEVICES) > 0;
+            } catch (SettingNotFoundException snfe) {
+            }
+
+            if (mVibrateInputDevicesSetting) {
+                if (!mInputDeviceListenerRegistered) {
+                    mInputDeviceListenerRegistered = true;
+                    mIm.registerInputDeviceListener(this, mH);
+                }
+            } else {
+                if (mInputDeviceListenerRegistered) {
+                    mInputDeviceListenerRegistered = false;
+                    mIm.unregisterInputDeviceListener(this);
+                }
+            }
+
+            updateInputDeviceVibrators();
+        }
+    }
+
+    private void updateInputDeviceVibrators() {
+        synchronized (mVibrations) {
+            doCancelVibrateLocked();
+
+            synchronized (mInputDeviceVibrators) {
+                mInputDeviceVibrators.clear();
+                if (mVibrateInputDevicesSetting) {
+                    int[] ids = mIm.getInputDeviceIds();
+                    for (int i = 0; i < ids.length; i++) {
+                        InputDevice device = mIm.getInputDevice(ids[i]);
+                        Vibrator vibrator = device.getVibrator();
+                        if (vibrator.hasVibrator()) {
+                            mInputDeviceVibrators.add(vibrator);
+                        }
+                    }
+                }
+            }
+
+            startNextVibrationLocked();
+        }
+    }
+
+    @Override
+    public void onInputDeviceAdded(int deviceId) {
+        updateInputDeviceVibrators();
+    }
+
+    @Override
+    public void onInputDeviceChanged(int deviceId) {
+        updateInputDeviceVibrators();
+    }
+
+    @Override
+    public void onInputDeviceRemoved(int deviceId) {
+        updateInputDeviceVibrators();
+    }
+
+    private boolean doVibratorExists() {
+        synchronized (mInputDeviceVibrators) {
+            return !mInputDeviceVibrators.isEmpty() || vibratorExists();
+        }
+    }
+
+    private void doVibratorOn(long millis) {
+        synchronized (mInputDeviceVibrators) {
+            final int vibratorCount = mInputDeviceVibrators.size();
+            if (vibratorCount != 0) {
+                for (int i = 0; i < vibratorCount; i++) {
+                    mInputDeviceVibrators.get(i).vibrate(millis);
+                }
+            } else {
+                vibratorOn(millis);
+            }
+        }
+    }
+
+    private void doVibratorOff() {
+        synchronized (mInputDeviceVibrators) {
+            final int vibratorCount = mInputDeviceVibrators.size();
+            if (vibratorCount != 0) {
+                for (int i = 0; i < vibratorCount; i++) {
+                    mInputDeviceVibrators.get(i).cancel();
+                }
+            } else {
+                vibratorOff();
+            }
+        }
+    }
+
     private class VibrateThread extends Thread {
         final Vibration mVibration;
         boolean mDone;
@@ -350,7 +483,7 @@
                         // duration is saved for delay() at top of loop
                         duration = pattern[index++];
                         if (duration > 0) {
-                            VibratorService.this.vibratorOn(duration);
+                            VibratorService.this.doVibratorOn(duration);
                         }
                     } else {
                         if (repeat < 0) {
@@ -394,15 +527,4 @@
             }
         }
     };
-
-    private Handler mH = new Handler();
-
-    private final Context mContext;
-    private final PowerManager.WakeLock mWakeLock;
-
-    volatile VibrateThread mThread;
-
-    native static boolean vibratorExists();
-    native static void vibratorOn(long milliseconds);
-    native static void vibratorOff();
 }
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 31aa21e..889fbe4 100644
--- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -16,6 +16,7 @@
 
 package com.android.server.accessibility;
 
+import com.android.server.accessibility.TouchExplorer.GestureListener;
 import com.android.server.input.InputFilter;
 
 import android.content.Context;
@@ -36,6 +37,8 @@
 
     private final Context mContext;
 
+    private final GestureListener mGestureListener;
+
     /**
      * This is an interface for explorers that take a {@link MotionEvent}
      * stream and perform touch exploration of the screen content.
@@ -64,11 +67,13 @@
     }
 
     private TouchExplorer mTouchExplorer;
+
     private int mTouchscreenSourceDeviceId;
 
-    public AccessibilityInputFilter(Context context) {
+    public AccessibilityInputFilter(Context context, GestureListener gestureListener) {
         super(context.getMainLooper());
         mContext = context;
+        mGestureListener = gestureListener;
     }
 
     @Override
@@ -76,7 +81,7 @@
         if (DEBUG) {
             Slog.d(TAG, "Accessibility input filter installed.");
         }
-        mTouchExplorer = new TouchExplorer(this, mContext);
+        mTouchExplorer = new TouchExplorer(this, mContext, mGestureListener);
         super.onInstalled();
     }
 
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index c99aa02..754a4dd 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,11 +16,14 @@
 
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
+import static android.accessibilityservice.AccessibilityServiceInfo.INCLUDE_NOT_IMPORTANT_VIEWS;
+
 import android.Manifest;
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
-import android.accessibilityservice.IEventListener;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -35,6 +38,7 @@
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -55,8 +59,7 @@
 import android.view.accessibility.IAccessibilityManagerClient;
 
 import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.HandlerCaller.SomeArgs;
+import com.android.server.accessibility.TouchExplorer.GestureListener;
 import com.android.server.wm.WindowManagerService;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -80,7 +83,7 @@
  * @hide
  */
 public class AccessibilityManagerService extends IAccessibilityManager.Stub
-        implements HandlerCaller.Callback {
+        implements GestureListener {
 
     private static final boolean DEBUG = false;
 
@@ -93,12 +96,8 @@
 
     private static final int OWN_PROCESS_ID = android.os.Process.myPid();
 
-    private static final int DO_SET_SERVICE_INFO = 10;
-
     private static int sNextWindowId;
 
-    final HandlerCaller mCaller;
-
     final Context mContext;
 
     final Object mLock = new Object();
@@ -152,7 +151,7 @@
             int eventType = message.arg1;
 
             synchronized (mLock) {
-                notifyEventListenerLocked(service, eventType);
+                notifyAccessibilityEventLocked(service, eventType);
             }
         }
     };
@@ -165,7 +164,6 @@
     public AccessibilityManagerService(Context context) {
         mContext = context;
         mPackageManager = mContext.getPackageManager();
-        mCaller = new HandlerCaller(context, this);
         mWindowManagerService = (WindowManagerService) ServiceManager.getService(
                 Context.WINDOW_SERVICE);
         mSecurityPolicy = new SecurityPolicy();
@@ -395,32 +393,6 @@
         }
     }
 
-    public void executeMessage(Message message) {
-        switch (message.what) {
-            case DO_SET_SERVICE_INFO: {
-                SomeArgs arguments = ((SomeArgs) message.obj);
-
-                AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
-                Service service = (Service) arguments.arg2;
-
-                synchronized (mLock) {
-                    // If the XML manifest had data to configure the service its info
-                    // should be already set. In such a case update only the dynamically
-                    // configurable properties.
-                    AccessibilityServiceInfo oldInfo = service.mAccessibilityServiceInfo;
-                    if (oldInfo != null) {
-                        oldInfo.updateDynamicallyConfigurableProperties(info);
-                        service.setDynamicallyConfigurableProperties(oldInfo);
-                    } else {
-                        service.setDynamicallyConfigurableProperties(info);
-                    }
-                }
-            } return;
-            default:
-                Slog.w(LOG_TAG, "Unknown message type: " + message.what);
-        }
-    }
-
     public int addAccessibilityInteractionConnection(IWindow windowToken,
             IAccessibilityInteractionConnection connection) throws RemoteException {
         synchronized (mLock) {
@@ -455,7 +427,7 @@
         }
     }
 
-    public void registerUiTestAutomationService(IEventListener listener,
+    public void registerUiTestAutomationService(IAccessibilityServiceClient serviceClient,
             AccessibilityServiceInfo accessibilityServiceInfo) {
         mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
                 FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE);
@@ -480,18 +452,45 @@
         }
         // Hook the automation service up.
         mUiAutomationService = new Service(componentName, accessibilityServiceInfo, true);
-        mUiAutomationService.onServiceConnected(componentName, listener.asBinder());
+        mUiAutomationService.onServiceConnected(componentName, serviceClient.asBinder());
     }
 
-    public void unregisterUiTestAutomationService(IEventListener listener) {
+    public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
         synchronized (mLock) {
             // Automation service is not bound, so pretend it died to perform clean up.
-            if (mUiAutomationService != null) {
+            if (mUiAutomationService != null
+                    && mUiAutomationService.mServiceInterface == serviceClient) {
                 mUiAutomationService.binderDied();
             }
         }
     }
 
+    @Override
+    public void onGesture(int gestureId) {
+        synchronized (mLock) {
+            final boolean dispatched = notifyGestureLocked(gestureId, false);
+            if (!dispatched) {
+                notifyGestureLocked(gestureId, true);
+            }
+        }
+    }
+
+    private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
+        final int serviceCount = mServices.size();
+        for (int i = 0; i < serviceCount; i++) {
+            Service service = mServices.get(i);
+            if (service.mIsDefault == isDefault) {
+                try {
+                    service.mServiceInterface.onGesture(gestureId);
+                    return true;
+                } catch (RemoteException re) {
+                    Slog.e(LOG_TAG, "Error dispatching gesture.");
+                }
+            }
+        }
+        return false;
+    }
+
     /**
      * Removes an AccessibilityInteractionConnection.
      *
@@ -588,13 +587,13 @@
     }
 
     /**
-     * Notifies a service for a scheduled event given the event type.
+     * Notifies an accessibility service client for a scheduled event given the event type.
      *
-     * @param service The service.
+     * @param service The service client.
      * @param eventType The type of the event to dispatch.
      */
-    private void notifyEventListenerLocked(Service service, int eventType) {
-        IEventListener listener = service.mServiceInterface;
+    private void notifyAccessibilityEventLocked(Service service, int eventType) {
+        IAccessibilityServiceClient listener = service.mServiceInterface;
 
         // If the service died/was disabled while the message for dispatching
         // the accessibility event was propagating the listener may be null.
@@ -699,6 +698,11 @@
             return false;
         }
 
+        if (!event.isImportantForAccessibility()
+                && !service.mIncludeNotImportantViews) {
+            return false;
+        }
+
         int eventType = event.getEventType();
         if ((service.mEventTypes & eventType) != eventType) {
             return false;
@@ -864,7 +868,7 @@
             if (!mHasInputFilter) {
                 mHasInputFilter = true;
                 if (mInputFilter == null) {
-                    mInputFilter = new AccessibilityInputFilter(mContext);
+                    mInputFilter = new AccessibilityInputFilter(mContext, this);
                 }
                 mWindowManagerService.setInputFilter(mInputFilter);
             }
@@ -942,7 +946,7 @@
 
         IBinder mService;
 
-        IEventListener mServiceInterface;
+        IAccessibilityServiceClient mServiceInterface;
 
         int mEventTypes;
 
@@ -952,6 +956,8 @@
 
         boolean mIsDefault;
 
+        boolean mIncludeNotImportantViews;
+
         long mNotificationTimeout;
 
         ComponentName mComponentName;
@@ -983,6 +989,7 @@
                         mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
             } else {
                 mCanRetrieveScreenContent = true;
+                mIncludeNotImportantViews = true;
             }
             setDynamicallyConfigurableProperties(accessibilityServiceInfo);
         }
@@ -995,7 +1002,17 @@
                 mPackageNames.addAll(Arrays.asList(packageNames));
             }
             mNotificationTimeout = info.notificationTimeout;
-            mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
+            mIsDefault = (info.flags & DEFAULT) != 0;
+
+            final int targetSdkVersion =
+                info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion;
+            // TODO: Uncomment this line and remove the line below when JellyBean
+            // SDK version is finalized.
+            // if (targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
+            if (targetSdkVersion > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+                mIncludeNotImportantViews =
+                    (info.flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+            }
 
             synchronized (mLock) {
                 tryAddServiceLocked(this);
@@ -1043,13 +1060,33 @@
             return (mEventTypes != 0 && mFeedbackType != 0 && mService != null);
         }
 
-        public void setServiceInfo(AccessibilityServiceInfo info) {
-            mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
+        @Override
+        public AccessibilityServiceInfo getServiceInfo() {
+            synchronized (mLock) {
+                return mAccessibilityServiceInfo;
+            }
         }
 
+        @Override
+        public void setServiceInfo(AccessibilityServiceInfo info) {
+            synchronized (mLock) {
+                // If the XML manifest had data to configure the service its info
+                // should be already set. In such a case update only the dynamically
+                // configurable properties.
+                AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
+                if (oldInfo != null) {
+                    oldInfo.updateDynamicallyConfigurableProperties(info);
+                    setDynamicallyConfigurableProperties(oldInfo);
+                } else {
+                    setDynamicallyConfigurableProperties(info);
+                }
+            }
+        }
+
+        @Override
         public void onServiceConnected(ComponentName componentName, IBinder service) {
             mService = service;
-            mServiceInterface = IEventListener.Stub.asInterface(service);
+            mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
             try {
                 mServiceInterface.setConnection(this, mId);
                 synchronized (mLock) {
@@ -1060,6 +1097,7 @@
             }
         }
 
+        @Override
         public float findAccessibilityNodeInfoByViewId(int accessibilityWindowId,
                 long accessibilityNodeId, int viewId, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
@@ -1078,11 +1116,13 @@
                     }
                 }
             }
+            final int flags = (mIncludeNotImportantViews) ?
+                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
             final int interrogatingPid = Binder.getCallingPid();
             final long identityToken = Binder.clearCallingIdentity();
             try {
                 connection.findAccessibilityNodeInfoByViewId(accessibilityNodeId, viewId,
-                        interactionId, callback, interrogatingPid, interrogatingTid);
+                        interactionId, callback, flags, interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
                 if (DEBUG) {
                     Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId().");
@@ -1093,6 +1133,7 @@
             return getCompatibilityScale(resolvedWindowId);
         }
 
+        @Override
         public float findAccessibilityNodeInfosByText(int accessibilityWindowId,
                 long accessibilityNodeId, String text, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
@@ -1112,11 +1153,13 @@
                     }
                 }
             }
+            final int flags = (mIncludeNotImportantViews) ?
+                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
             final int interrogatingPid = Binder.getCallingPid();
             final long identityToken = Binder.clearCallingIdentity();
             try {
                 connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
-                        interactionId, callback, interrogatingPid, interrogatingTid);
+                        interactionId, callback, flags, interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
                 if (DEBUG) {
                     Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");
@@ -1127,10 +1170,47 @@
             return getCompatibilityScale(resolvedWindowId);
         }
 
+        @Override
         public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
                 long accessibilityNodeId, int interactionId,
-                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid,
-                int prefetchFlags)
+                IAccessibilityInteractionConnectionCallback callback, int flags,
+                long interrogatingTid) throws RemoteException {
+            final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
+            IAccessibilityInteractionConnection connection = null;
+            synchronized (mLock) {
+                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
+                final boolean permissionGranted =
+                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
+                if (!permissionGranted) {
+                    return 0;
+                } else {
+                    connection = getConnectionLocked(resolvedWindowId);
+                    if (connection == null) {
+                        return 0;
+                    }
+                }
+            }
+            final int allFlags = flags | ((mIncludeNotImportantViews) ?
+                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0);
+            final int interrogatingPid = Binder.getCallingPid();
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
+                        interactionId, callback, allFlags, interrogatingPid, interrogatingTid);
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            return getCompatibilityScale(resolvedWindowId);
+        }
+
+        @Override
+        public float findFocus(int accessibilityWindowId, long accessibilityNodeId,
+                int focusType, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
                 throws RemoteException {
             final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
             IAccessibilityInteractionConnection connection = null;
@@ -1147,14 +1227,16 @@
                     }
                 }
             }
+            final int flags = (mIncludeNotImportantViews) ?
+                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
             final int interrogatingPid = Binder.getCallingPid();
             final long identityToken = Binder.clearCallingIdentity();
             try {
-                connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
-                        interactionId, callback, prefetchFlags, interrogatingPid, interrogatingTid);
+                connection.findFocus(accessibilityNodeId, interactionId, focusType, callback,
+                        flags, interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
                 if (DEBUG) {
-                    Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
+                    Slog.e(LOG_TAG, "Error calling findAccessibilityFocus()");
                 }
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
@@ -1162,6 +1244,44 @@
             return getCompatibilityScale(resolvedWindowId);
         }
 
+        @Override
+        public float focusSearch(int accessibilityWindowId, long accessibilityNodeId,
+                int direction, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
+                throws RemoteException {
+            final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
+            IAccessibilityInteractionConnection connection = null;
+            synchronized (mLock) {
+                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
+                final boolean permissionGranted =
+                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
+                if (!permissionGranted) {
+                    return 0;
+                } else {
+                    connection = getConnectionLocked(resolvedWindowId);
+                    if (connection == null) {
+                        return 0;
+                    }
+                }
+            }
+            final int flags = (mIncludeNotImportantViews) ?
+                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
+            final int interrogatingPid = Binder.getCallingPid();
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                connection.focusSearch(accessibilityNodeId, interactionId, direction, callback,
+                        flags, interrogatingPid, interrogatingTid);
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            return getCompatibilityScale(resolvedWindowId);
+        }
+
+        @Override
         public boolean performAccessibilityAction(int accessibilityWindowId,
                 long accessibilityNodeId, int action, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) {
@@ -1179,11 +1299,13 @@
                     }
                 }
             }
+            final int flags = (mIncludeNotImportantViews) ?
+                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
             final int interrogatingPid = Binder.getCallingPid();
             final long identityToken = Binder.clearCallingIdentity();
             try {
                 connection.performAccessibilityAction(accessibilityNodeId, action, interactionId,
-                        callback, interrogatingPid, interrogatingTid);
+                        callback, flags, interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
                 if (DEBUG) {
                     Slog.e(LOG_TAG, "Error calling performAccessibilityAction()");
@@ -1263,22 +1385,35 @@
     }
 
     final class SecurityPolicy {
-        private static final int VALID_ACTIONS = AccessibilityNodeInfo.ACTION_FOCUS
-            | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS | AccessibilityNodeInfo.ACTION_SELECT
-            | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
+        private static final int VALID_ACTIONS =
+            AccessibilityNodeInfo.ACTION_CLICK
+            | AccessibilityNodeInfo.ACTION_FOCUS
+            | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS
+            | AccessibilityNodeInfo.ACTION_SELECT
+            | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION
+            | AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS
+            | AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
 
         private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
-            AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED
-            | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
-            | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
-            | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_SELECTED
+            AccessibilityEvent.TYPE_VIEW_CLICKED
+            | AccessibilityEvent.TYPE_VIEW_FOCUSED
+            | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
+            | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+            | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED
+            | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
+            | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+            | AccessibilityEvent.TYPE_VIEW_SELECTED
             | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
             | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
-            | AccessibilityEvent.TYPE_VIEW_SCROLLED;
+            | AccessibilityEvent.TYPE_VIEW_SCROLLED
+            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
+            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
 
         private static final int RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES =
-            AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
-            | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT;
+            AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+            | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
+            | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
 
         private int mRetrievalAlowingWindowId;
 
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index d07aa7a..5d01c77 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -20,17 +20,25 @@
 import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START;
 
 import android.content.Context;
+import android.gesture.Gesture;
+import android.gesture.GestureLibraries;
+import android.gesture.GestureLibrary;
+import android.gesture.GesturePoint;
+import android.gesture.GestureStroke;
+import android.gesture.Prediction;
 import android.os.Handler;
 import android.util.Slog;
 import android.view.MotionEvent;
+import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicy;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 
-import com.android.server.accessibility.AccessibilityInputFilter.Explorer;
 import com.android.server.input.InputFilter;
+import com.android.internal.R;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 
 /**
@@ -54,34 +62,24 @@
  *
  * @hide
  */
-public class TouchExplorer implements Explorer {
+public class TouchExplorer {
+
     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";
+    private static final String LOG_TAG = "TouchExplorer";
 
     // 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;
-
-    // Invalid pointer ID.
-    private static final int INVALID_POINTER_ID = -1;
+    private static final int STATE_GESTURE_DETECTING = 0x00000005;
 
     // The time slop in milliseconds for activating an item after it has
     // been touch explored. Tapping on an item within this slop will perform
     // a click and tapping and holding down a long press.
     private static final long ACTIVATION_TIME_SLOP = 2000;
 
-    // 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 MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
@@ -92,6 +90,14 @@
     // Constant referring to the ids bits of all pointers.
     private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
 
+    // This constant captures the current implementation detail that
+    // pointer IDs are between 0 and 31 inclusive (subject to change).
+    // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
+    public static final int MAX_POINTER_COUNT = 32;
+
+    // Invalid pointer ID.
+    public static final int INVALID_POINTER_ID = -1;
+
     // Temporary array for storing pointer IDs.
     private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT];
 
@@ -103,10 +109,6 @@
     // 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;
@@ -132,21 +134,48 @@
     // Command for delayed sending of a long press.
     private final PerformLongPressDelayed mPerformLongPressDelayed;
 
+    private VelocityTracker mVelocityTracker;
+
+    private final ReceivedPointerTracker mReceivedPointerTracker;
+
+    private final InjectedPointerTracker mInjectedPointerTracker;
+
+    private final GestureListener mGestureListener;
+
+    /**
+     * Callback for gesture detection.
+     */
+    public interface GestureListener {
+
+        /**
+         * Called when a given gesture was performed.
+         *
+         * @param gestureId The gesture id.
+         */
+        public void onGesture(int gestureId);
+    }
+
     /**
      * 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) {
+    public TouchExplorer(InputFilter inputFilter, Context context,
+            GestureListener gestureListener) {
+        mGestureListener = gestureListener;
+        mReceivedPointerTracker = new ReceivedPointerTracker(context);
+        mInjectedPointerTracker = new InjectedPointerTracker();
         mInputFilter = inputFilter;
         mTouchExplorationTapSlop =
-            ViewConfiguration.get(context).getScaledTouchExplorationTapSlop();
-        mPointerTracker = new PointerTracker(context);
+            ViewConfiguration.get(context).getScaledTouchExploreTapSlop();
         mHandler = new Handler(context.getMainLooper());
         mSendHoverDelayed = new SendHoverDelayed();
         mPerformLongPressDelayed = new PerformLongPressDelayed();
         mAccessibilityManager = AccessibilityManager.getInstance(context);
+        mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
+        mGestureLibrary.setOrientationStyle(4);
+        mGestureLibrary.load();
     }
 
     public void clear(MotionEvent event, int policyFlags) {
@@ -154,18 +183,14 @@
         clear();
     }
 
-    /**
-     * {@inheritDoc}
-     */
     public void onMotionEvent(MotionEvent event, int policyFlags) {
         if (DEBUG) {
-            Slog.d(LOG_TAG_RECEIVED, "Received event: " + event + ", policyFlags=0x"
+            Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x"
                     + Integer.toHexString(policyFlags));
-            Slog.d(LOG_TAG_STATE, getStateSymbolicName(mCurrentState));
+            Slog.d(LOG_TAG, getStateSymbolicName(mCurrentState));
         }
 
-        // Keep track of the pointers's state.
-        mPointerTracker.onReceivedMotionEvent(event);
+        mReceivedPointerTracker.onMotionEvent(event);
 
         switch(mCurrentState) {
             case STATE_TOUCH_EXPLORING: {
@@ -177,9 +202,11 @@
             case STATE_DELEGATING: {
                 handleMotionEventStateDelegating(event, policyFlags);
             } break;
-            default: {
+            case STATE_GESTURE_DETECTING: {
+                handleMotionEventGestureDetecting(event, policyFlags);
+            } break;
+            default:
                 throw new IllegalStateException("Illegal state: " + mCurrentState);
-            }
         }
     }
 
@@ -190,8 +217,14 @@
      * @param policyFlags The policy flags associated with the event.
      */
     private void handleMotionEventStateTouchExploring(MotionEvent event, int policyFlags) {
-        PointerTracker pointerTracker = mPointerTracker;
-        final int activePointerCount = pointerTracker.getActivePointerCount();
+        ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
+        InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
+        final int activePointerCount = receivedTracker.getActivePointerCount();
+
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(event);
 
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
@@ -205,9 +238,9 @@
                         mSendHoverDelayed.remove();
                         mPerformLongPressDelayed.remove();
                         // Send a hover for every finger down so the user gets feedback.
-                        final int pointerId = pointerTracker.getPrimaryActivePointerId();
+                        final int pointerId = receivedTracker.getPrimaryActivePointerId();
                         final int pointerIdBits = (1 << pointerId);
-                        final int lastAction = pointerTracker.getLastInjectedHoverAction();
+                        final int lastAction = injectedTracker.getLastInjectedHoverAction();
 
                         // Deliver hover enter with a delay to have a change to detect
                         // whether the user actually starts a scrolling gesture.
@@ -232,7 +265,7 @@
 
                         // If the down is in the time slop => schedule a long press.
                         final long pointerDownTime =
-                            pointerTracker.getReceivedPointerDownTime(pointerId);
+                            receivedTracker.getReceivedPointerDownTime(pointerId);
                         final long lastExploreTime = mLastTouchExploreEvent.getEventTime();
                         final long deltaTimeExplore = pointerDownTime - lastExploreTime;
                         if (deltaTimeExplore <= ACTIVATION_TIME_SLOP) {
@@ -247,7 +280,7 @@
                 }
             } break;
             case MotionEvent.ACTION_MOVE: {
-                final int pointerId = pointerTracker.getPrimaryActivePointerId();
+                final int pointerId = receivedTracker.getPrimaryActivePointerId();
                 final int pointerIndex = event.findPointerIndex(pointerId);
                 final int pointerIdBits = (1 << pointerId);
                 switch (activePointerCount) {
@@ -258,13 +291,27 @@
                         // 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)
+                            final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId)
                                 - event.getX(pointerIndex);
-                            final float deltaY = pointerTracker.getReceivedPointerDownY(pointerId)
+                            final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId)
                                 - event.getY(pointerIndex);
                             final double moveDelta = Math.hypot(deltaX, deltaY);
 
                             if (moveDelta > mTouchExplorationTapSlop) {
+
+                                mVelocityTracker.computeCurrentVelocity(1000);
+                                final float maxAbsVelocity = Math.max(
+                                        Math.abs(mVelocityTracker.getXVelocity(pointerId)),
+                                        Math.abs(mVelocityTracker.getYVelocity(pointerId)));
+                                // TODO: Tune the velocity cut off and add a constant.
+                                if (maxAbsVelocity > 1000) {
+                                    clear(event, policyFlags);
+                                    mCurrentState = STATE_GESTURE_DETECTING;
+                                    event.setAction(MotionEvent.ACTION_DOWN);
+                                    handleMotionEventGestureDetecting(event, policyFlags);
+                                    return;
+                                }
+
                                 mTouchExploreGestureInProgress = true;
                                 sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
                                 // Make sure the scheduled down/move event is sent.
@@ -272,7 +319,7 @@
                                 mPerformLongPressDelayed.remove();
                                 // If we have transitioned to exploring state from another one
                                 // we need to send a hover enter event here.
-                                final int lastAction = mPointerTracker.getLastInjectedHoverAction();
+                                final int lastAction = injectedTracker.getLastInjectedHoverAction();
                                 if (lastAction == MotionEvent.ACTION_HOVER_EXIT) {
                                     sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER,
                                             pointerIdBits, policyFlags);
@@ -355,12 +402,12 @@
             } break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_POINTER_UP: {
-                final int pointerId = pointerTracker.getLastReceivedUpPointerId();
+                final int pointerId = receivedTracker.getLastReceivedUpPointerId();
                 final int pointerIdBits = (1 << pointerId);
                 switch (activePointerCount) {
                     case 0: {
                         // If the pointer that went up was not active we have nothing to do.
-                        if (!pointerTracker.wasLastReceivedUpPointerActive()) {
+                        if (!receivedTracker.wasLastReceivedUpPointerActive()) {
                             break;
                         }
 
@@ -381,7 +428,7 @@
                         if (mLastTouchExploreEvent != null) {
                             // If the down was not in the time slop => nothing else to do.
                             final long eventTime =
-                                pointerTracker.getLastReceivedUpPointerDownTime();
+                                receivedTracker.getLastReceivedUpPointerDownTime();
                             final long exploreTime = mLastTouchExploreEvent.getEventTime();
                             final long deltaTime = eventTime - exploreTime;
                             if (deltaTime > ACTIVATION_TIME_SLOP) {
@@ -422,14 +469,22 @@
                         }
                     } break;
                 }
+                if (mVelocityTracker != null) {
+                    mVelocityTracker.clear();
+                    mVelocityTracker = null;
+                }
             } break;
             case MotionEvent.ACTION_CANCEL: {
                 mSendHoverDelayed.remove();
                 mPerformLongPressDelayed.remove();
-                final int pointerId = pointerTracker.getPrimaryActivePointerId();
+                final int pointerId = receivedTracker.getPrimaryActivePointerId();
                 final int pointerIdBits = (1 << pointerId);                
                 ensureHoverExitSent(event, pointerIdBits, policyFlags);
                 clear();
+                if (mVelocityTracker != null) {
+                    mVelocityTracker.clear();
+                    mVelocityTracker = null;
+                }
             } break;
         }
     }
@@ -455,7 +510,7 @@
                 sendDownForAllActiveNotInjectedPointers(event, policyFlags);
             } break;
             case MotionEvent.ACTION_MOVE: {
-                final int activePointerCount = mPointerTracker.getActivePointerCount();
+                final int activePointerCount = mReceivedPointerTracker.getActivePointerCount();
                 switch (activePointerCount) {
                     case 1: {
                         // do nothing
@@ -487,7 +542,7 @@
                 }
             } break;
             case MotionEvent.ACTION_POINTER_UP: {
-                final int activePointerCount = mPointerTracker.getActivePointerCount();
+                final int activePointerCount = mReceivedPointerTracker.getActivePointerCount();
                 switch (activePointerCount) {
                     case 1: {
                         // Send an event to the end of the drag gesture.
@@ -525,7 +580,8 @@
             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();
+                final int notInjectedCount = getNotInjectedActivePointerCount(
+                        mReceivedPointerTracker, mInjectedPointerTracker);
                 if (notInjectedCount > 0) {
                     MotionEvent prototype = MotionEvent.obtain(event);
                     sendDownForAllActiveNotInjectedPointers(prototype, policyFlags);
@@ -533,7 +589,7 @@
             } break;
             case MotionEvent.ACTION_POINTER_UP: {
                 // No active pointers => go to initial state.
-                if (mPointerTracker.getActivePointerCount() == 0) {
+                if (mReceivedPointerTracker.getActivePointerCount() == 0) {
                     mCurrentState = STATE_TOUCH_EXPLORING;
                 }
             } break;
@@ -545,6 +601,72 @@
         sendMotionEventStripInactivePointers(event, policyFlags);
     }
 
+    private float mPreviousX;
+    private float mPreviousY;
+
+    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
+
+    private static final int TOUCH_TOLERANCE = 3;
+    private static final float MIN_PREDICTION_SCORE = 2.0f;
+
+    private GestureLibrary mGestureLibrary;
+
+    private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) {
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN: {
+                final float x = event.getX();
+                final float y = event.getY();
+                mPreviousX = x;
+                mPreviousY = y;
+                mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+            } break;
+            case MotionEvent.ACTION_MOVE: {
+                final float x = event.getX();
+                final float y = event.getY();
+                final float dX = Math.abs(x - mPreviousX);
+                final float dY = Math.abs(y - mPreviousY);
+                if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) {
+                    mPreviousX = x;
+                    mPreviousY = y;
+                    mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+                }
+            } break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_POINTER_UP: {
+                float x = event.getX();
+                float y = event.getY();
+                mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+
+                Gesture gesture = new Gesture();
+                gesture.addStroke(new GestureStroke(mStrokeBuffer));
+
+                ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture);
+                if (!predictions.isEmpty()) {
+                    Prediction bestPrediction = predictions.get(0);
+                    if (bestPrediction.score >= MIN_PREDICTION_SCORE) {
+                        if (DEBUG) {
+                            Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: "
+                                    + bestPrediction.score);
+                        }
+                        try {
+                            final int gestureId = Integer.parseInt(bestPrediction.name);
+                            mGestureListener.onGesture(gestureId);
+                        } catch (NumberFormatException nfe) {
+                            Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name);
+                        }
+                    }
+                }
+
+                mStrokeBuffer.clear();
+                mCurrentState = STATE_TOUCH_EXPLORING;
+            } break;
+            case MotionEvent.ACTION_CANCEL: {
+                mStrokeBuffer.clear();
+                mCurrentState = STATE_TOUCH_EXPLORING;
+            } break;
+        }
+    }
+
     /**
      * 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.
@@ -553,14 +675,15 @@
      * @param policyFlags The policy flags associated with the event.
      */
     private void sendDownForAllActiveNotInjectedPointers(MotionEvent prototype, int policyFlags) {
-        final PointerTracker pointerTracker = mPointerTracker;
+        ReceivedPointerTracker receivedPointers = mReceivedPointerTracker;
+        InjectedPointerTracker injectedPointers = mInjectedPointerTracker;
         int pointerIdBits = 0;
         final int pointerCount = prototype.getPointerCount();
 
         // Find which pointers are already injected.
         for (int i = 0; i < pointerCount; i++) {
             final int pointerId = prototype.getPointerId(i);
-            if (pointerTracker.isInjectedPointerDown(pointerId)) {
+            if (injectedPointers.isInjectedPointerDown(pointerId)) {
                 pointerIdBits |= (1 << pointerId);
             }
         }
@@ -569,11 +692,11 @@
         for (int i = 0; i < pointerCount; i++) {
             final int pointerId = prototype.getPointerId(i);
             // Skip inactive pointers.
-            if (!pointerTracker.isActivePointer(pointerId)) {
+            if (!receivedPointers.isActivePointer(pointerId)) {
                 continue;
             }
             // Do not send event for already delivered pointers.
-            if (pointerTracker.isInjectedPointerDown(pointerId)) {
+            if (injectedPointers.isInjectedPointerDown(pointerId)) {
                 continue;
             }
             pointerIdBits |= (1 << pointerId);
@@ -590,7 +713,7 @@
      * @param policyFlags The policy flags associated with the event.
      */
     private void ensureHoverExitSent(MotionEvent prototype, int pointerIdBits, int policyFlags) {
-        final int lastAction = mPointerTracker.getLastInjectedHoverAction();
+        final int lastAction = mInjectedPointerTracker.getLastInjectedHoverAction();
         if (lastAction != MotionEvent.ACTION_HOVER_EXIT) {
             sendMotionEvent(prototype, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits,
                     policyFlags);
@@ -605,13 +728,13 @@
      * @param policyFlags The policy flags associated with the event.
      */
     private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
-        final PointerTracker pointerTracker = mPointerTracker;
+        final InjectedPointerTracker injectedTracked = mInjectedPointerTracker;
         int pointerIdBits = 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)) {
+            if (!injectedTracked.isInjectedPointerDown(pointerId)) {
                 continue;
             }
             pointerIdBits |= (1 << pointerId);
@@ -627,18 +750,18 @@
      * @param policyFlags The policy flags associated with the event.
      */
     private void sendMotionEventStripInactivePointers(MotionEvent prototype, int policyFlags) {
-        PointerTracker pointerTracker = mPointerTracker;
+        ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
 
         // All pointers active therefore we just inject the event as is.
-        if (prototype.getPointerCount() == pointerTracker.getActivePointerCount()) {
+        if (prototype.getPointerCount() == receivedTracker.getActivePointerCount()) {
             sendMotionEvent(prototype, prototype.getAction(), ALL_POINTER_ID_BITS, 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()) {
+        if (receivedTracker.getActivePointerCount() == 0
+                && !receivedTracker.wasLastReceivedUpPointerActive()) {
             return;
         }
 
@@ -647,7 +770,7 @@
         final int actionMasked = prototype.getActionMasked();
         final int actionPointerId = prototype.getPointerId(prototype.getActionIndex());
         if (actionMasked != MotionEvent.ACTION_MOVE) {
-            if (!pointerTracker.isActiveOrWasLastActiveUpPointer(actionPointerId)) {
+            if (!receivedTracker.isActiveOrWasLastActiveUpPointer(actionPointerId)) {
                 return;
             }
         }
@@ -658,7 +781,7 @@
         final int pointerCount = prototype.getPointerCount();
         for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) {
             final int pointerId = prototype.getPointerId(pointerIndex);
-            if (pointerTracker.isActiveOrWasLastActiveUpPointer(pointerId)) {
+            if (receivedTracker.isActiveOrWasLastActiveUpPointer(pointerId)) {
                 pointerIdBits |= (1 << pointerId);
             }
         }
@@ -700,19 +823,20 @@
         if (action == MotionEvent.ACTION_DOWN) {
             event.setDownTime(event.getEventTime());
         } else {
-            event.setDownTime(mPointerTracker.getLastInjectedDownEventTime());
+            event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime());
         }
 
         if (DEBUG) {
-            Slog.d(LOG_TAG_INJECTED, "Injecting event: " + event + ", policyFlags=0x"
+            Slog.d(LOG_TAG, "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);
 
+        mInjectedPointerTracker.onMotionEvent(event);
+
         if (event != prototype) {
             event.recycle();
         }
@@ -730,9 +854,9 @@
         switch (actionMasked) {
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_POINTER_DOWN: {
-                PointerTracker pointerTracker = mPointerTracker;
+                InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
                 // Compute the action based on how many down pointers are injected.
-                if (pointerTracker.getInjectedPointerDownCount() == 0) {
+                if (injectedTracker.getInjectedPointerDownCount() == 0) {
                     return MotionEvent.ACTION_DOWN;
                 } else {
                     return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
@@ -740,9 +864,9 @@
                 }
             }
             case MotionEvent.ACTION_POINTER_UP: {
-                PointerTracker pointerTracker = mPointerTracker;
+                InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
                 // Compute the action based on how many down pointers are injected.
-                if (pointerTracker.getInjectedPointerDownCount() == 1) {
+                if (injectedTracker.getInjectedPointerDownCount() == 1) {
                     return MotionEvent.ACTION_UP;
                 } else {
                     return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
@@ -761,9 +885,9 @@
      * @return True if the gesture is a dragging one.
      */
     private boolean isDraggingGesture(MotionEvent event) {
-        PointerTracker pointerTracker = mPointerTracker;
+        ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
         int[] pointerIds = mTempPointerIds;
-        pointerTracker.populateActivePointerIds(pointerIds);
+        receivedTracker.populateActivePointerIds(pointerIds);
 
         final int firstPtrIndex = event.findPointerIndex(pointerIds[0]);
         final int secondPtrIndex = event.findPointerIndex(pointerIds[1]);
@@ -775,9 +899,9 @@
 
         // Check if the pointers are moving in the same direction.
         final float firstDeltaX =
-            firstPtrX - pointerTracker.getReceivedPointerDownX(firstPtrIndex);
+            firstPtrX - receivedTracker.getReceivedPointerDownX(firstPtrIndex);
         final float firstDeltaY =
-            firstPtrY - pointerTracker.getReceivedPointerDownY(firstPtrIndex);
+            firstPtrY - receivedTracker.getReceivedPointerDownY(firstPtrIndex);
 
         if (firstDeltaX == 0 && firstDeltaY == 0) {
             return true;
@@ -791,9 +915,9 @@
             (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
 
         final float secondDeltaX =
-            secondPtrX - pointerTracker.getReceivedPointerDownX(secondPtrIndex);
+            secondPtrX - receivedTracker.getReceivedPointerDownX(secondPtrIndex);
         final float secondDeltaY =
-            secondPtrY - pointerTracker.getReceivedPointerDownY(secondPtrIndex);
+            secondPtrY - receivedTracker.getReceivedPointerDownY(secondPtrIndex);
 
         if (secondDeltaX == 0 && secondDeltaY == 0) {
             return true;
@@ -832,7 +956,8 @@
     public void clear() {
         mSendHoverDelayed.remove();
         mPerformLongPressDelayed.remove();
-        mPointerTracker.clear();
+        mReceivedPointerTracker.clear();
+        mInjectedPointerTracker.clear();
         mLastTouchExploreEvent = null;
         mCurrentState = STATE_TOUCH_EXPLORING;
         mTouchExploreGestureInProgress = false;
@@ -853,27 +978,253 @@
                 return "STATE_DRAGGING";
             case STATE_DELEGATING:
                 return "STATE_DELEGATING";
+            case STATE_GESTURE_DETECTING:
+                return "STATE_GESTURE_DETECTING";
             default:
                 throw new IllegalArgumentException("Unknown state: " + state);
         }
     }
 
     /**
-     * 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.
+     * @return The number of non injected active pointers.
      */
-    class PointerTracker {
-        private static final String LOG_TAG = "PointerTracker";
+    private int getNotInjectedActivePointerCount(ReceivedPointerTracker receivedTracker,
+            InjectedPointerTracker injectedTracker) {
+        final int pointerState = receivedTracker.getActivePointers()
+                & ~injectedTracker.getInjectedPointersDown();
+        return Integer.bitCount(pointerState);
+    }
+
+    /**
+     * Class for delayed sending of long press.
+     */
+    private final class PerformLongPressDelayed implements Runnable {
+        private MotionEvent mEvent;
+        private int mPolicyFlags;
+
+        public void post(MotionEvent prototype, int policyFlags, long delay) {
+            mEvent = MotionEvent.obtain(prototype);
+            mPolicyFlags = policyFlags;
+            mHandler.postDelayed(this, delay);
+        }
+
+        public void remove() {
+            if (isPenidng()) {
+                mHandler.removeCallbacks(this);
+                clear();
+            }
+        }
+
+        private boolean isPenidng() {
+            return (mEvent != null);
+        }
+
+        @Override
+        public void run() {
+            mCurrentState = STATE_DELEGATING;
+            // Make sure the scheduled hover exit is delivered.
+            mSendHoverDelayed.remove();
+            final int pointerId = mReceivedPointerTracker.getPrimaryActivePointerId();
+            final int pointerIdBits = (1 << pointerId);
+            ensureHoverExitSent(mEvent, pointerIdBits, mPolicyFlags);
+
+            sendDownForAllActiveNotInjectedPointers(mEvent, mPolicyFlags);
+            mTouchExploreGestureInProgress = false;
+            mLastTouchExploreEvent = null;
+            clear();
+        }
+
+        private void clear() {
+            if (!isPenidng()) {
+                return;
+            }
+            mEvent.recycle();
+            mEvent = null;
+            mPolicyFlags = 0;
+        }
+    }
+
+    /**
+     * Class for delayed sending of hover events.
+     */
+    private final class SendHoverDelayed implements Runnable {
+        private MotionEvent mEvent;
+        private int mAction;
+        private int mPointerIdBits;
+        private int mPolicyFlags;
+
+        public void post(MotionEvent prototype, int action, int pointerIdBits, int policyFlags,
+                long delay) {
+            remove();
+            mEvent = MotionEvent.obtain(prototype);
+            mAction = action;
+            mPointerIdBits = pointerIdBits;
+            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;
+            mPointerIdBits = -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");
+                }
+            }
+
+            sendMotionEvent(mEvent, mAction, mPointerIdBits, mPolicyFlags);
+            clear();
+        }
+    }
+
+    @Override
+    public String toString() {
+        return LOG_TAG;
+    }
+
+    class InjectedPointerTracker {
+        private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker";
+
+        // Keep track of which pointers sent to the system are down.
+        private int mInjectedPointersDown;
+
+        // 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;
+
+        /**
+         * Processes an injected {@link MotionEvent} event.
+         *
+         * @param event The event to process.
+         */
+        public void onMotionEvent(MotionEvent event) {
+            final int action = event.getActionMasked();
+            switch (action) {
+                case MotionEvent.ACTION_DOWN:
+                case MotionEvent.ACTION_POINTER_DOWN: {
+                    final int pointerId = event.getPointerId(event.getActionIndex());
+                    final int pointerFlag = (1 << pointerId);
+                    mInjectedPointersDown |= pointerFlag;
+                    mLastInjectedDownEventTime = event.getDownTime();
+                } break;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_POINTER_UP: {
+                    final int pointerId = event.getPointerId(event.getActionIndex());
+                    final int pointerFlag = (1 << pointerId);
+                    mInjectedPointersDown &= ~pointerFlag;
+                    if (mInjectedPointersDown == 0) {
+                        mLastInjectedDownEventTime = 0;
+                    }
+                } 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_TRACKER, "Injected pointer: " + toString());
+            }
+        }
+
+        /**
+         * Clears the internals state.
+         */
+        public void clear() {
+            mInjectedPointersDown = 0;
+        }
+
+        /**
+         * @return The time of the last injected down event.
+         */
+        public long getLastInjectedDownEventTime() {
+            return mLastInjectedDownEventTime;
+        }
+
+        /**
+         * @return The number of down pointers injected to the view hierarchy.
+         */
+        public int getInjectedPointerDownCount() {
+            return Integer.bitCount(mInjectedPointersDown);
+        }
+
+        /**
+         * @return The bits of the injected pointers that are down.
+         */
+        public int getInjectedPointersDown() {
+            return mInjectedPointersDown;
+        }
+
+        /**
+         * 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 action of the last injected hover event.
+         */
+        public int getLastInjectedHoverAction() {
+            return mLastInjectedHoverEventAction;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("=========================");
+            builder.append("\nDown pointers #");
+            builder.append(Integer.bitCount(mInjectedPointersDown));
+            builder.append(" [ ");
+            for (int i = 0; i < MAX_POINTER_COUNT; i++) {
+                if ((mInjectedPointersDown & i) != 0) {
+                    builder.append(i);
+                    builder.append(" ");
+                }
+            }
+            builder.append("]");
+            builder.append("\n=========================");
+            return builder.toString();
+        }
+    }
+
+    class ReceivedPointerTracker {
+        private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker";
 
         // The coefficient by which to multiply
         // ViewConfiguration.#getScaledTouchSlop()
@@ -902,26 +1253,19 @@
         // 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 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;
+        private float mLastReceivedUpPointerDownX;
+        private float mLastReceivedUpPointerDownY;
 
         /**
          * Creates a new instance.
          *
          * @param context Context for looking up resources.
          */
-        public PointerTracker(Context context) {
+        public ReceivedPointerTracker(Context context) {
             mThresholdActivePointer =
                 ViewConfiguration.get(context).getScaledTouchSlop() * COEFFICIENT_ACTIVE_POINTER;
         }
@@ -937,10 +1281,11 @@
             mActivePointers = 0;
             mPrimaryActivePointerId = 0;
             mHasMovingActivePointer = false;
-            mInjectedPointersDown = 0;
             mLastReceivedUpPointerDownTime = 0;
             mLastReceivedUpPointerId = 0;
             mLastReceivedUpPointerActive = false;
+            mLastReceivedUpPointerDownX = 0;
+            mLastReceivedUpPointerDownY = 0;
         }
 
         /**
@@ -948,12 +1293,10 @@
          *
          * @param event The event to process.
          */
-        public void onReceivedMotionEvent(MotionEvent event) {
+        public void onMotionEvent(MotionEvent event) {
             final int action = event.getActionMasked();
             switch (action) {
                 case MotionEvent.ACTION_DOWN: {
-                    // New gesture so restart tracking injected down pointers.
-                    mInjectedPointersDown = 0;
                     handleReceivedPointerDown(event.getActionIndex(), event);
                 } break;
                 case MotionEvent.ACTION_POINTER_DOWN: {
@@ -970,39 +1313,7 @@
                 } 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(event.getActionIndex(), event);
-                    mLastInjectedDownEventTime = event.getDownTime();
-                } break;
-                case MotionEvent.ACTION_POINTER_DOWN: {
-                    handleInjectedPointerDown(event.getActionIndex(), event);
-                } break;
-                case MotionEvent.ACTION_UP: {
-                    handleInjectedPointerUp(event.getActionIndex(), 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());
+                Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer: " + toString());
             }
         }
 
@@ -1014,6 +1325,13 @@
         }
 
         /**
+         * @return The bits of the pointers that are active.
+         */
+        public int getActivePointers() {
+            return mActivePointers;
+        }
+
+        /**
          * @return The number of down input  pointers that are active.
          */
         public int getActivePointerCount() {
@@ -1032,24 +1350,6 @@
         }
 
         /**
-         * 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.
@@ -1108,27 +1408,27 @@
             return mLastReceivedUpPointerId;
         }
 
+
+        /**
+         * @return The down X of the last received pointer that went up.
+         */
+        public float getLastReceivedUpPointerDownX() {
+            return mLastReceivedUpPointerDownX;
+        }
+
+        /**
+         * @return The down Y of the last received pointer that went up.
+         */
+        public float getLastReceivedUpPointerDownY() {
+            return mLastReceivedUpPointerDownY;
+        }
+
         /**
          * @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>
@@ -1147,18 +1447,10 @@
         }
 
         /**
-         * @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) {
+        public boolean isActiveOrWasLastActiveUpPointer(int pointerId) {
             return (isActivePointer(pointerId)
                     || (mLastReceivedUpPointerId == pointerId
                             && mLastReceivedUpPointerActive));
@@ -1177,6 +1469,8 @@
             mLastReceivedUpPointerId = 0;
             mLastReceivedUpPointerDownTime = 0;
             mLastReceivedUpPointerActive = false;
+            mLastReceivedUpPointerDownX = 0;
+            mLastReceivedUpPointerDownX = 0;
 
             mReceivedPointersDown |= pointerFlag;
             mReceivedPointerDownX[pointerId] = event.getX(pointerIndex);
@@ -1217,6 +1511,8 @@
             mLastReceivedUpPointerId = pointerId;
             mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId);
             mLastReceivedUpPointerActive = isActivePointer(pointerId);
+            mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId];
+            mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId];
 
             mReceivedPointersDown &= ~pointerFlag;
             mActivePointers &= ~pointerFlag;
@@ -1233,33 +1529,6 @@
         }
 
         /**
-         * 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;
-        }
-
-        /**
-         * 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;
-            if (mInjectedPointersDown == 0) {
-                mLastInjectedDownEventTime = 0;
-            }
-        }
-
-        /**
          * Detects the active pointers in an event.
          *
          * @param event The event to examine.
@@ -1348,117 +1617,4 @@
             return builder.toString();
         }
     }
-
-    /**
-     * Class for delayed sending of long press.
-     */
-    private final class PerformLongPressDelayed implements Runnable {
-        private MotionEvent mEvent;
-        private int mPolicyFlags;
-
-        public void post(MotionEvent prototype, int policyFlags, long delay) {
-            mEvent = MotionEvent.obtain(prototype);
-            mPolicyFlags = policyFlags;
-            mHandler.postDelayed(this, delay);
-        }
-
-        public void remove() {
-            if (isPenidng()) {
-                mHandler.removeCallbacks(this);
-                clear();
-            }
-        }
-
-        private boolean isPenidng() {
-            return (mEvent != null);
-        }
-
-        @Override
-        public void run() {
-            mCurrentState = STATE_DELEGATING;
-            // Make sure the scheduled hover exit is delivered.
-            mSendHoverDelayed.remove();
-            final int pointerId = mPointerTracker.getPrimaryActivePointerId();
-            final int pointerIdBits = (1 << pointerId);
-            ensureHoverExitSent(mEvent, pointerIdBits, mPolicyFlags);
-
-            sendDownForAllActiveNotInjectedPointers(mEvent, mPolicyFlags);
-            mTouchExploreGestureInProgress = false;
-            mLastTouchExploreEvent = null;
-            clear();
-        }
-
-        private void clear() {
-            if (!isPenidng()) {
-                return;
-            }
-            mEvent.recycle();
-            mEvent = null;
-            mPolicyFlags = 0;
-        }
-    }
-
-    /**
-     * 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 mPointerIdBits;
-        private int mPolicyFlags;
-
-        public void post(MotionEvent prototype, int action, int pointerIdBits, int policyFlags,
-                long delay) {
-            remove();
-            mEvent = MotionEvent.obtain(prototype);
-            mAction = action;
-            mPointerIdBits = pointerIdBits;
-            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;
-            mPointerIdBits = -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");
-                }
-            }
-
-            sendMotionEvent(mEvent, mAction, mPointerIdBits, mPolicyFlags);
-            clear();
-        }
-    }
 }
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index ce7671f..e819432 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -105,6 +105,12 @@
             mTempInputDevicesChangedListenersToNotify =
                     new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only
 
+    // State for vibrator tokens.
+    private Object mVibratorLock = new Object();
+    private HashMap<IBinder, VibratorToken> mVibratorTokens =
+            new HashMap<IBinder, VibratorToken>();
+    private int mNextVibratorTokenValue;
+
     // State for the currently installed input filter.
     final Object mInputFilterLock = new Object();
     InputFilter mInputFilter; // guarded by mInputFilterLock
@@ -142,6 +148,9 @@
             InputChannel fromChannel, InputChannel toChannel);
     private static native void nativeSetPointerSpeed(int ptr, int speed);
     private static native void nativeSetShowTouches(int ptr, boolean enabled);
+    private static native void nativeVibrate(int ptr, int deviceId, long[] pattern,
+            int repeat, int token);
+    private static native void nativeCancelVibrate(int ptr, int deviceId, int token);
     private static native String nativeDump(int ptr);
     private static native void nativeMonitor(int ptr);
 
@@ -792,6 +801,65 @@
         return result;
     }
 
+    // Binder call
+    @Override
+    public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
+        if (repeat >= pattern.length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+        VibratorToken v;
+        synchronized (mVibratorLock) {
+            v = mVibratorTokens.get(token);
+            if (v == null) {
+                v = new VibratorToken(deviceId, token, mNextVibratorTokenValue++);
+                try {
+                    token.linkToDeath(v, 0);
+                } catch (RemoteException ex) {
+                    // give up
+                    throw new RuntimeException(ex);
+                }
+                mVibratorTokens.put(token, v);
+            }
+        }
+
+        synchronized (v) {
+            v.mVibrating = true;
+            nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue);
+        }
+    }
+
+    // Binder call
+    @Override
+    public void cancelVibrate(int deviceId, IBinder token) {
+        VibratorToken v;
+        synchronized (mVibratorLock) {
+            v = mVibratorTokens.get(token);
+            if (v == null || v.mDeviceId != deviceId) {
+                return; // nothing to cancel
+            }
+        }
+
+        cancelVibrateIfNeeded(v);
+    }
+
+    void onVibratorTokenDied(VibratorToken v) {
+        synchronized (mVibratorLock) {
+            mVibratorTokens.remove(v.mToken);
+        }
+
+        cancelVibrateIfNeeded(v);
+    }
+
+    private void cancelVibrateIfNeeded(VibratorToken v) {
+        synchronized (v) {
+            if (v.mVibrating) {
+                nativeCancelVibrate(mPtr, v.mDeviceId, v.mTokenValue);
+                v.mVibrating = false;
+            }
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
@@ -1108,4 +1176,26 @@
             }
         }
     }
+
+    private final class VibratorToken implements DeathRecipient {
+        public final int mDeviceId;
+        public final IBinder mToken;
+        public final int mTokenValue;
+
+        public boolean mVibrating;
+
+        public VibratorToken(int deviceId, IBinder token, int tokenValue) {
+            mDeviceId = deviceId;
+            mToken = token;
+            mTokenValue = tokenValue;
+        }
+
+        @Override
+        public void binderDied() {
+            if (DEBUG) {
+                Slog.d(TAG, "Vibrator token died.");
+            }
+            onVibratorTokenDied(this);
+        }
+    }
 }
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index fa62e497..5408c42 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -50,6 +50,7 @@
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.telephony.TelephonyManager.SIM_STATE_READY;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static com.android.internal.util.ArrayUtils.appendInt;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
 import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readBooleanAttribute;
@@ -1216,6 +1217,23 @@
     }
 
     @Override
+    public int[] getAppsWithPolicy(int policy) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        int[] appIds = new int[0];
+        synchronized (mRulesLock) {
+            for (int i = 0; i < mAppPolicy.size(); i++) {
+                final int appId = mAppPolicy.keyAt(i);
+                final int appPolicy = mAppPolicy.valueAt(i);
+                if (appPolicy == policy) {
+                    appIds = appendInt(appIds, appId);
+                }
+            }
+        }
+        return appIds;
+    }
+
+    @Override
     public void registerListener(INetworkPolicyListener listener) {
         // TODO: create permission for observing network policy
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index dfa058c..b97d7fd 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -16,12 +16,14 @@
 
 package com.android.server.pm;
 
+import static android.Manifest.permission.GRANT_REVOKE_PERMISSIONS;
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
-import static android.Manifest.permission.GRANT_REVOKE_PERMISSIONS;
+import static com.android.internal.util.ArrayUtils.appendInt;
+import static com.android.internal.util.ArrayUtils.removeInt;
 import static libcore.io.OsConstants.S_ISLNK;
 
 import com.android.internal.app.IMediaContainerService;
@@ -1452,22 +1454,6 @@
         }
     }
 
-    static int[] appendInt(int[] cur, int val) {
-        if (cur == null) {
-            return new int[] { val };
-        }
-        final int N = cur.length;
-        for (int i=0; i<N; i++) {
-            if (cur[i] == val) {
-                return cur;
-            }
-        }
-        int[] ret = new int[N+1];
-        System.arraycopy(cur, 0, ret, 0, N);
-        ret[N] = val;
-        return ret;
-    }
-
     static int[] appendInts(int[] cur, int[] add) {
         if (add == null) return cur;
         if (cur == null) return add;
@@ -1478,26 +1464,6 @@
         return cur;
     }
 
-    static int[] removeInt(int[] cur, int val) {
-        if (cur == null) {
-            return null;
-        }
-        final int N = cur.length;
-        for (int i=0; i<N; i++) {
-            if (cur[i] == val) {
-                int[] ret = new int[N-1];
-                if (i > 0) {
-                    System.arraycopy(cur, 0, ret, 0, i);
-                }
-                if (i < (N-1)) {
-                    System.arraycopy(cur, i + 1, ret, i, N - i - 1);
-                }
-                return ret;
-            }
-        }
-        return cur;
-    }
-
     static int[] removeInts(int[] cur, int[] rem) {
         if (rem == null) return cur;
         if (cur == null) return cur;
@@ -6615,6 +6581,7 @@
             oldPackage = mPackages.get(pkgName);
             if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
                     != PackageManager.SIGNATURE_MATCH) {
+                Slog.w(TAG, "New package has a different signature: " + pkgName);
                 res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
                 return;
             }
diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp
index f1536fd..3795074 100644
--- a/services/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/jni/com_android_server_input_InputManagerService.cpp
@@ -1226,6 +1226,38 @@
     im->setShowTouches(enabled);
 }
 
+static void nativeVibrate(JNIEnv* env,
+        jclass clazz, jint ptr, jint deviceId, jlongArray patternObj,
+        jint repeat, jint token) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    size_t patternSize = env->GetArrayLength(patternObj);
+    if (patternSize > MAX_VIBRATE_PATTERN_SIZE) {
+        ALOGI("Skipped requested vibration because the pattern size is %d "
+                "which is more than the maximum supported size of %d.",
+                patternSize, MAX_VIBRATE_PATTERN_SIZE);
+        return; // limit to reasonable size
+    }
+
+    jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical(
+            patternObj, NULL));
+    nsecs_t pattern[patternSize];
+    for (size_t i = 0; i < patternSize; i++) {
+        pattern[i] = max(jlong(0), min(patternMillis[i],
+                MAX_VIBRATE_PATTERN_DELAY_NSECS / 1000000LL)) * 1000000LL;
+    }
+    env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT);
+
+    im->getInputManager()->getReader()->vibrate(deviceId, pattern, patternSize, repeat, token);
+}
+
+static void nativeCancelVibrate(JNIEnv* env,
+        jclass clazz, jint ptr, jint deviceId, jint token) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    im->getInputManager()->getReader()->cancelVibrate(deviceId, token);
+}
+
 static jstring nativeDump(JNIEnv* env, jclass clazz, jint ptr) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
@@ -1287,6 +1319,10 @@
             (void*) nativeSetPointerSpeed },
     { "nativeSetShowTouches", "(IZ)V",
             (void*) nativeSetShowTouches },
+    { "nativeVibrate", "(II[JII)V",
+            (void*) nativeVibrate },
+    { "nativeCancelVibrate", "(III)V",
+            (void*) nativeCancelVibrate },
     { "nativeDump", "(I)Ljava/lang/String;",
             (void*) nativeDump },
     { "nativeMonitor", "(I)V",
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 486a924..a124c7f 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -35,6 +35,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.HashMap;
 import java.util.List;
 
@@ -208,6 +209,27 @@
     protected static final int EVENT_RIL_CONNECTED = BASE + 5;
     protected static final int EVENT_DISCONNECT_ALL = BASE + 6;
 
+    private static final int CMD_TO_STRING_COUNT = EVENT_DISCONNECT_ALL + 1;
+    private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
+    static {
+        sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT";
+        sCmdToString[EVENT_SETUP_DATA_CONNECTION_DONE - BASE] =
+                "EVENT_SETUP_DATA_CONNECTION_DONE";
+        sCmdToString[EVENT_GET_LAST_FAIL_DONE - BASE] = "EVENT_GET_LAST_FAIL_DONE";
+        sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE";
+        sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT";
+        sCmdToString[EVENT_RIL_CONNECTED - BASE] = "EVENT_RIL_CONNECTED";
+        sCmdToString[EVENT_DISCONNECT_ALL - BASE] = "EVENT_DISCONNECT_ALL";
+    }
+    protected static String cmdToString(int cmd) {
+        cmd -= BASE;
+        if ((cmd >= 0) && (cmd < sCmdToString.length)) {
+            return sCmdToString[cmd];
+        } else {
+            return null;
+        }
+    }
+
     //***** Tag IDs for EventLog
     protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100;
 
@@ -242,6 +264,7 @@
     protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm,
             DataConnectionTracker dct) {
         super(name);
+        setProcessedMessagesSize(100);
         if (DBG) log("DataConnection constructor E");
         this.phone = phone;
         this.mDataConnectionTracker = dct;
@@ -1188,26 +1211,65 @@
                 new DisconnectParams(reason, onCompletedMsg)));
     }
 
+    /**
+     * @return the string for msg.what as our info.
+     */
+    @Override
+    protected String getMessageInfo(Message msg) {
+        String info = null;
+        info = cmdToString(msg.what);
+        if (info == null) {
+            info = DataConnectionAc.cmdToString(msg.what);
+        }
+        return info;
+    }
+
+    /**
+     * Convert a System.currentTimeMillis() value to a time of day value.
+     *
+     * @param millis since the epoch (1/1/1970)
+     * @return String representation of the time.
+     */
+    private String timeMillisToTimeOfDay(long millis) {
+        Calendar c = Calendar.getInstance();
+        if (millis >= 0) {
+            c.setTimeInMillis(millis);
+            return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c);
+        } else {
+            return Long.toString(millis);
+        }
+    }
+    /**
+     * Dump the current state.
+     *
+     * @param fd
+     * @param pw
+     * @param args
+     */
+    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("DataConnection name=" + getName() + ":");
+        pw.print("DataConnection ");
+        super.dump(fd, pw, args);
         pw.println(" mApnList=" + mApnList);
+        pw.flush();
         pw.println(" mDataConnectionTracker=" + mDataConnectionTracker);
         pw.println(" mApn=" + mApn);
         pw.println(" mTag=" + mTag);
+        pw.flush();
         pw.println(" phone=" + phone);
         pw.println(" mRilVersion=" + mRilVersion);
         pw.println(" cid=" + cid);
+        pw.flush();
         pw.println(" mLinkProperties=" + mLinkProperties);
+        pw.flush();
         pw.println(" mCapabilities=" + mCapabilities);
-        pw.println(" createTime=" + createTime);
-        pw.println(" lastFailTime=" + lastFailTime);
+        pw.println(" createTime=" + timeMillisToTimeOfDay(createTime));
+        pw.println(" lastFailTime=" + timeMillisToTimeOfDay(lastFailTime));
         pw.println(" lastFailCause=" + lastFailCause);
+        pw.flush();
         pw.println(" mRetryOverride=" + mRetryOverride);
         pw.println(" mRefCount=" + mRefCount);
         pw.println(" userData=" + userData);
-        pw.println(" total messages=" + getProcessedMessagesCount());
-        for (int i=0; i < getProcessedMessagesSize(); i++) {
-            pw.printf("  msg[%d]=%s\n", i, getProcessedMessageInfo(i));
-        }
+        pw.flush();
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
index a9f2cd1..4744ff0 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
@@ -82,6 +82,51 @@
     public static final int REQ_GET_RECONNECT_INTENT = BASE + 26;
     public static final int RSP_GET_RECONNECT_INTENT = BASE + 27;
 
+    private static final int CMD_TO_STRING_COUNT = RSP_GET_RECONNECT_INTENT + 1;
+    private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
+    static {
+        sCmdToString[REQ_IS_INACTIVE - BASE] = "REQ_IS_INACTIVE";
+        sCmdToString[RSP_IS_INACTIVE - BASE] = "RSP_IS_INACTIVE";
+        sCmdToString[REQ_GET_CID - BASE] = "REQ_GET_CID";
+        sCmdToString[RSP_GET_CID - BASE] = "RSP_GET_CID";
+        sCmdToString[REQ_GET_APNSETTING - BASE] = "REQ_GET_APNSETTING";
+        sCmdToString[RSP_GET_APNSETTING - BASE] = "RSP_GET_APNSETTING";
+        sCmdToString[REQ_GET_LINK_PROPERTIES - BASE] = "REQ_GET_LINK_PROPERTIES";
+        sCmdToString[RSP_GET_LINK_PROPERTIES - BASE] = "RSP_GET_LINK_PROPERTIES";
+        sCmdToString[REQ_SET_LINK_PROPERTIES_HTTP_PROXY - BASE] =
+                "REQ_SET_LINK_PROPERTIES_HTTP_PROXY";
+        sCmdToString[RSP_SET_LINK_PROPERTIES_HTTP_PROXY - BASE] =
+                "RSP_SET_LINK_PROPERTIES_HTTP_PROXY";
+        sCmdToString[REQ_GET_LINK_CAPABILITIES - BASE] = "REQ_GET_LINK_CAPABILITIES";
+        sCmdToString[RSP_GET_LINK_CAPABILITIES - BASE] = "RSP_GET_LINK_CAPABILITIES";
+        sCmdToString[REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE - BASE] =
+                "REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE";
+        sCmdToString[RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE - BASE] =
+                "RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE";
+        sCmdToString[REQ_RESET - BASE] = "REQ_RESET";
+        sCmdToString[RSP_RESET - BASE] = "RSP_RESET";
+        sCmdToString[REQ_GET_REFCOUNT - BASE] = "REQ_GET_REFCOUNT";
+        sCmdToString[RSP_GET_REFCOUNT - BASE] = "RSP_GET_REFCOUNT";
+        sCmdToString[REQ_ADD_APNCONTEXT - BASE] = "REQ_ADD_APNCONTEXT";
+        sCmdToString[RSP_ADD_APNCONTEXT - BASE] = "RSP_ADD_APNCONTEXT";
+        sCmdToString[REQ_REMOVE_APNCONTEXT - BASE] = "REQ_REMOVE_APNCONTEXT";
+        sCmdToString[RSP_REMOVE_APNCONTEXT - BASE] = "RSP_REMOVE_APNCONTEXT";
+        sCmdToString[REQ_GET_APNCONTEXT_LIST - BASE] = "REQ_GET_APNCONTEXT_LIST";
+        sCmdToString[RSP_GET_APNCONTEXT_LIST - BASE] = "RSP_GET_APNCONTEXT_LIST";
+        sCmdToString[REQ_SET_RECONNECT_INTENT - BASE] = "REQ_SET_RECONNECT_INTENT";
+        sCmdToString[RSP_SET_RECONNECT_INTENT - BASE] = "RSP_SET_RECONNECT_INTENT";
+        sCmdToString[REQ_GET_RECONNECT_INTENT - BASE] = "REQ_GET_RECONNECT_INTENT";
+        sCmdToString[RSP_GET_RECONNECT_INTENT - BASE] = "RSP_GET_RECONNECT_INTENT";
+    }
+    protected static String cmdToString(int cmd) {
+        cmd -= BASE;
+        if ((cmd >= 0) && (cmd < sCmdToString.length)) {
+            return sCmdToString[cmd];
+        } else {
+            return AsyncChannel.cmdToString(cmd + BASE);
+        }
+    }
+
     /**
      * enum used to notify action taken or necessary to be
      * taken after the link property is changed.
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index ae01c75..5eac1f2 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -44,7 +44,7 @@
     private final static String TAG = "NotificationTestList";
 
     NotificationManager mNM;
-    Vibrator mVibrator = new Vibrator();
+    Vibrator mVibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE);
     Handler mHandler = new Handler();
 
     long mActivityCreateTime = System.currentTimeMillis();