Merge "Adding support for traversing the content of a node info at granularity."
diff --git a/api/current.txt b/api/current.txt
index e2452c1..4699139 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23928,7 +23928,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 performAccessibilityAction(int, android.os.Bundle);
     method public boolean performClick();
     method public boolean performHapticFeedback(int);
     method public boolean performHapticFeedback(int, int);
@@ -24997,6 +24997,7 @@
     method public int getChildCount();
     method public java.lang.CharSequence getClassName();
     method public java.lang.CharSequence getContentDescription();
+    method public java.lang.CharSequence[] getGranularities();
     method public java.lang.CharSequence getPackageName();
     method public android.view.accessibility.AccessibilityNodeInfo getParent();
     method public java.lang.CharSequence getText();
@@ -25017,6 +25018,7 @@
     method public static android.view.accessibility.AccessibilityNodeInfo obtain();
     method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.accessibility.AccessibilityNodeInfo);
     method public boolean performAction(int);
+    method public boolean performAction(int, android.os.Bundle);
     method public void recycle();
     method public void setAccessibilityFocused(boolean);
     method public void setBoundsInParent(android.graphics.Rect);
@@ -25029,6 +25031,7 @@
     method public void setEnabled(boolean);
     method public void setFocusable(boolean);
     method public void setFocused(boolean);
+    method public void setGranularities(java.lang.CharSequence[]);
     method public void setLongClickable(boolean);
     method public void setPackageName(java.lang.CharSequence);
     method public void setParent(android.view.View);
@@ -25041,12 +25044,15 @@
     method public void setText(java.lang.CharSequence);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final java.lang.String ACTION_ARGUMENT_GRANULARITY = "ACTION_ARGUMENT_GRANULARITY";
     field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
     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 = 16; // 0x10
     field public static final int ACTION_FOCUS = 1; // 0x1
     field public static final int ACTION_LONG_CLICK = 32; // 0x20
+    field public static final int ACTION_NEXT_AT_GRANULARITY = 256; // 0x100
+    field public static final int ACTION_PREVIOUS_AT_GRANULARITY = 512; // 0x200
     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
@@ -25059,7 +25065,7 @@
     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);
+    method public boolean performAction(int, int, android.os.Bundle);
   }
 
   public class AccessibilityRecord {
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 6e85665..dd50f3c 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -16,6 +16,7 @@
 
 package android.accessibilityservice;
 
+import android.os.Bundle;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
@@ -147,14 +148,15 @@
      *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
      *     to start from the root.
      * @param action The action to perform.
+     * @param arguments Optional action arguments.
      * @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 Whether the action was performed.
      */
     boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId,
-        int action, int interactionId, IAccessibilityInteractionConnectionCallback callback,
-        long threadId);
+        int action, in Bundle arguments, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback, long threadId);
 
     /**
      * @return The associated accessibility service info.
diff --git a/core/java/android/accessibilityservice/UiTestAutomationBridge.java b/core/java/android/accessibilityservice/UiTestAutomationBridge.java
index 1697df0..4d4bfeb 100644
--- a/core/java/android/accessibilityservice/UiTestAutomationBridge.java
+++ b/core/java/android/accessibilityservice/UiTestAutomationBridge.java
@@ -19,6 +19,7 @@
 import android.accessibilityservice.AccessibilityService.Callbacks;
 import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -444,10 +445,12 @@
      *
      * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id).
      * @param action The action to perform.
+     * @param arguments Optional action arguments.
      * @return Whether the action was performed.
      */
-    public boolean performAccessibilityActionInActiveWindow(long accessibilityNodeId, int action) {
-        return performAccessibilityAction(ACTIVE_WINDOW_ID, accessibilityNodeId, action);
+    public boolean performAccessibilityActionInActiveWindow(long accessibilityNodeId, int action,
+            Bundle arguments) {
+        return performAccessibilityAction(ACTIVE_WINDOW_ID, accessibilityNodeId, action, arguments);
     }
 
     /**
@@ -457,15 +460,16 @@
      *     {@link #ACTIVE_WINDOW_ID} to query the currently active window.
      * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id).
      * @param action The action to perform.
+     * @param arguments Optional action arguments.
      * @return Whether the action was performed.
      */
     public boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId,
-            int action) {
+            int action, Bundle arguments) {
         // Cache the id to avoid locking
         final int connectionId = mConnectionId;
         ensureValidConnection(connectionId);
         return AccessibilityInteractionClient.getInstance().performAccessibilityAction(connectionId,
-                accessibilityWindowId, accessibilityNodeId, action);
+                accessibilityWindowId, accessibilityNodeId, action, arguments);
     }
 
     /**
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index e3f5b96..7d569ad 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -18,6 +18,7 @@
 
 import static android.view.accessibility.AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS;
 
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -514,8 +515,9 @@
     }
 
     public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
-            int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
-            int interogatingPid, long interrogatingTid) {
+            Bundle arguments, int interactionId,
+            IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
+            long interrogatingTid) {
         Message message = mHandler.obtainMessage();
         message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
         message.arg1 = flags;
@@ -525,6 +527,7 @@
         args.argi2 = action;
         args.argi3 = interactionId;
         args.arg1 = callback;
+        args.arg2 = arguments;
         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
@@ -547,6 +550,7 @@
         final int interactionId = args.argi3;
         final IAccessibilityInteractionConnectionCallback callback =
             (IAccessibilityInteractionConnectionCallback) args.arg1;
+        Bundle arguments = (Bundle) args.arg2;
         mPool.release(args);
         boolean succeeded = false;
         try {
@@ -564,9 +568,10 @@
             if (target != null && target.isDisplayedOnScreen()) {
                 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
                 if (provider != null) {
-                    succeeded = provider.performAccessibilityAction(action, virtualDescendantId);
+                    succeeded = provider.performAction(virtualDescendantId, action,
+                            arguments);
                 } else if (virtualDescendantId == View.NO_ID) {
-                    succeeded = target.performAccessibilityAction(action);
+                    succeeded = target.performAccessibilityAction(action, arguments);
                 }
             }
         } finally {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ce02113..e6d7c02 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -39,6 +39,7 @@
 import android.graphics.Shader;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -6390,7 +6391,7 @@
      * @param action The action to perform.
      * @return Whether the action was performed.
      */
-    public boolean performAccessibilityAction(int action) {
+    public boolean performAccessibilityAction(int action, Bundle args) {
         switch (action) {
             case AccessibilityNodeInfo.ACTION_CLICK: {
                 if (isClickable()) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index da1f64b..e3681df 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5160,12 +5160,13 @@
 
         @Override
         public void performAccessibilityAction(long accessibilityNodeId, int action,
-                int interactionId, IAccessibilityInteractionConnectionCallback callback,
-                int flags, int interogatingPid, long interrogatingTid) {
+                Bundle arguments, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, int flags,
+                int interogatingPid, long interrogatingTid) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
-                    .performAccessibilityActionClientThread(accessibilityNodeId, action,
+                    .performAccessibilityActionClientThread(accessibilityNodeId, action, arguments,
                             interactionId, callback, flags, interogatingPid, interrogatingTid);
             } else {
                 // We cannot make the call and notify the caller so it does not wait.
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index f73faf3..24e90fd 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -19,6 +19,7 @@
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
@@ -408,17 +409,18 @@
      *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
      *     to start from the root.
      * @param action The action to perform.
+     * @param arguments Optional action arguments.
      * @return Whether the action was performed.
      */
     public boolean performAccessibilityAction(int connectionId, int accessibilityWindowId,
-            long accessibilityNodeId, int action) {
+            long accessibilityNodeId, int action, Bundle arguments) {
         try {
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
                 final boolean success = connection.performAccessibilityAction(
-                        accessibilityWindowId, accessibilityNodeId, action, interactionId, this,
-                        Thread.currentThread().getId());
+                        accessibilityWindowId, accessibilityNodeId, action, arguments,
+                        interactionId, this, Thread.currentThread().getId());
                 if (success) {
                     return getPerformAccessibilityActionResultAndClear(interactionId);
                 }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index c5f2062..7e565fc 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -17,9 +17,9 @@
 package android.view.accessibility;
 
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.TextUtils;
 import android.util.SparseLongArray;
 import android.view.View;
 
@@ -122,6 +122,80 @@
     public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
 
     /**
+     * Action that requests from the node to go to the next entity in its content
+     * at a given granularity. For example, move to the next word, link, etc.
+     * <p>
+     * <strong>Arguments:</strong>
+     * <ul>
+     * <li>
+     * {@link #ACTION_ARGUMENT_GRANULARITY}
+     * </li>
+     * <li>
+     * </p>
+     * <p>
+     * <strong>Example:</strong>
+     * <code><pre><p>
+     *   // Assume the first granularity was presented to the user and she is
+     *   // making an explicit action to traverse the node at that granularity.
+     *   CharSequence granularity = info.getGranularity(0);
+     *   Bundle arguments = new Bundle();
+     *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY, granularity);
+     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY, arguments);
+     * </code></pre></p>
+     * </li>
+     * </ul>
+     * </p>
+     * @see #setGranularities(CharSequence[])
+     * @see #getGranularities()
+     */
+    public static final int ACTION_NEXT_AT_GRANULARITY = 0x00000100;
+
+    /**
+     * Action that requests from the node to go to the previous entity in its content
+     * at a given granularity. For example, move to the next word, link, etc.
+     * <p>
+     * <strong>Arguments:</strong>
+     * <ul>
+     * <li>
+     *  {@link #ACTION_ARGUMENT_GRANULARITY}
+     * </li>
+     * <li>
+     * </p>
+     * <p>
+     * <strong>Example:</strong>
+     * <code><pre><p>
+     *   // Assume the first granularity was presented to the user and she is
+     *   // making an explicit action to traverse the node at that granularity.
+     *   CharSequence granularity = info.getGranularity(0);
+     *   Bundle arguments = new Bundle();
+     *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY, granularity);
+     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY, arguments);
+     * </code></pre></p>
+     * </li>
+     * </ul>
+     * </p>
+     * @see #setGranularities(CharSequence[])
+     * @see #getGranularities()
+     */
+    public static final int ACTION_PREVIOUS_AT_GRANULARITY = 0x00000200;
+
+    /**
+     * Argument for which content granularity to be used when traversing the node content.
+     * <p>
+     * <strong>Actions:</strong>
+     * <ul>
+     * <li>
+     * {@link #ACTION_PREVIOUS_AT_GRANULARITY}
+     * </li>
+     * <li>
+     * {@link #ACTION_PREVIOUS_AT_GRANULARITY}
+     * </li>
+     * </ul>
+     * </p>
+     */
+    public static final String ACTION_ARGUMENT_GRANULARITY = "ACTION_ARGUMENT_GRANULARITY";
+
+    /**
      * The input focus.
      */
     public static final int FOCUS_INPUT = 1;
@@ -231,9 +305,11 @@
     private CharSequence mText;
     private CharSequence mContentDescription;
 
-    private SparseLongArray mChildNodeIds = new SparseLongArray();
+    private final SparseLongArray mChildNodeIds = new SparseLongArray();
     private int mActions;
 
+    private CharSequence[] mGranularities;
+
     private int mConnectionId = UNDEFINED;
 
     /**
@@ -458,6 +534,32 @@
     }
 
     /**
+     * Sets the granularities for traversing the content of this node.
+     * <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 granularities The granularity names.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setGranularities(CharSequence[] granularities) {
+        enforceNotSealed();
+        mGranularities = granularities;
+    }
+
+    /**
+     * Gets the granularities for traversing the content of this node.
+     *
+     * @return The count.
+     */
+    public CharSequence[] getGranularities() {
+        return mGranularities;
+    }
+
+    /**
      * Performs an action on the node.
      * <p>
      *   <strong>Note:</strong> An action can be performed only if the request is made
@@ -475,7 +577,31 @@
             return false;
         }
         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
-        return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, action);
+        return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
+                action, null);
+    }
+
+    /**
+     * Performs an action on the node.
+     * <p>
+     *   <strong>Note:</strong> An action can be performed only if the request is made
+     *   from an {@link android.accessibilityservice.AccessibilityService}.
+     * </p>
+     *
+     * @param action The action to perform.
+     * @param arguments A bundle with additional arguments.
+     * @return True if the action was performed.
+     *
+     * @throws IllegalStateException If called outside of an AccessibilityService.
+     */
+    public boolean performAction(int action, Bundle arguments) {
+        enforceSealed();
+        if (!canPerformRequestOverConnection(mSourceNodeId)) {
+            return false;
+        }
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
+                action, arguments);
     }
 
     /**
@@ -1215,6 +1341,8 @@
         parcel.writeLong(mParentNodeId);
         parcel.writeInt(mConnectionId);
 
+        parcel.writeCharSequenceArray(mGranularities);
+
         SparseLongArray childIds = mChildNodeIds;
         final int childIdsSize = childIds.size();
         parcel.writeInt(childIdsSize);
@@ -1236,10 +1364,10 @@
 
         parcel.writeInt(mBooleanProperties);
 
-        TextUtils.writeToParcel(mPackageName, parcel, flags);
-        TextUtils.writeToParcel(mClassName, parcel, flags);
-        TextUtils.writeToParcel(mText, parcel, flags);
-        TextUtils.writeToParcel(mContentDescription, parcel, flags);
+        parcel.writeCharSequence(mPackageName);
+        parcel.writeCharSequence(mClassName);
+        parcel.writeCharSequence(mText);
+        parcel.writeCharSequence(mContentDescription);
 
         // Since instances of this class are fetched via synchronous i.e. blocking
         // calls in IPCs we always recycle as soon as the instance is marshaled.
@@ -1251,6 +1379,7 @@
      *
      * @param other The other instance.
      */
+    @SuppressWarnings("unchecked")
     private void init(AccessibilityNodeInfo other) {
         mSealed = other.mSealed;
         mSourceNodeId = other.mSourceNodeId;
@@ -1265,7 +1394,11 @@
         mContentDescription = other.mContentDescription;
         mActions= other.mActions;
         mBooleanProperties = other.mBooleanProperties;
-        mChildNodeIds = other.mChildNodeIds.clone();
+        mGranularities = other.mGranularities.clone();
+        final int otherChildIdCount = other.mChildNodeIds.size();
+        for (int i = 0; i < otherChildIdCount; i++) {
+            mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));    
+        }
     }
 
     /**
@@ -1280,6 +1413,8 @@
         mParentNodeId = parcel.readLong();
         mConnectionId = parcel.readInt();
 
+        mGranularities = parcel.readCharSequenceArray();
+
         SparseLongArray childIds = mChildNodeIds;
         final int childrenSize = parcel.readInt();
         for (int i = 0; i < childrenSize; i++) {
@@ -1301,10 +1436,10 @@
 
         mBooleanProperties = parcel.readInt();
 
-        mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
-        mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
-        mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
-        mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        mPackageName = parcel.readCharSequence();
+        mClassName = parcel.readCharSequence();
+        mText = parcel.readCharSequence();
+        mContentDescription = parcel.readCharSequence();
     }
 
     /**
@@ -1316,6 +1451,7 @@
         mParentNodeId = ROOT_NODE_ID;
         mWindowId = UNDEFINED;
         mConnectionId = UNDEFINED;
+        mGranularities = null;
         mChildNodeIds.clear();
         mBoundsInParent.set(0, 0, 0, 0);
         mBoundsInScreen.set(0, 0, 0, 0);
@@ -1394,6 +1530,17 @@
             builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
             builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
             builder.append("; mParentNodeId: " + mParentNodeId);
+
+            CharSequence[] granularities = mGranularities;
+            builder.append("; granularities: [");
+            for (int i = 0, count = granularities.length; i < count; i++) {
+                builder.append(granularities[i]);
+                if (i < count - 1) {
+                    builder.append(", ");
+                }
+            }
+            builder.append("]");
+
             SparseLongArray childIds = mChildNodeIds;
             builder.append("; childAccessibilityIds: [");
             for (int i = 0, count = childIds.size(); i < count; i++) {
@@ -1401,8 +1548,8 @@
                 if (i < count - 1) {
                     builder.append(", ");
                 }
-           }
-           builder.append("]");
+            }
+            builder.append("]");
         }
 
         builder.append("; boundsInParent: " + mBoundsInParent);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
index 19e35dd..ba6433f 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -17,6 +17,7 @@
 package android.view.accessibility;
 
 import android.accessibilityservice.AccessibilityService;
+import android.os.Bundle;
 import android.view.View;
 
 import java.util.List;
@@ -47,12 +48,13 @@
  *     getAccessibilityNodeProvider(
  *         if (mAccessibilityNodeProvider == null) {
  *             mAccessibilityNodeProvider = new AccessibilityNodeProvider() {
- *                 public boolean performAccessibilityAction(int action, int virtualDescendantId) {
+ *                 public boolean performAction(int action, int virtualDescendantId) {
  *                     // Implementation.
  *                     return false;
  *                 }
  *
- *                 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text, int virtualDescendantId) {
+ *                 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text,
+ *                         int virtualDescendantId) {
  *                     // Implementation.
  *                     return null;
  *                 }
@@ -99,15 +101,16 @@
      * host View, with the given <code>virtualViewId</code> or the host View itself
      * if <code>virtualViewId</code> equals to {@link View#NO_ID}.
      *
-     * @param action The action to perform.
      * @param virtualViewId A client defined virtual view id.
+     * @param action The action to perform.
+     * @param arguments Optional action arguments.
      * @return True if the action was performed.
      *
-     * @see View#performAccessibilityAction(int)
+     * @see View#performAccessibilityAction(int, Bundle)
      * @see #createAccessibilityNodeInfo(int)
      * @see AccessibilityNodeInfo
      */
-    public boolean performAccessibilityAction(int action, int virtualViewId) {
+    public boolean performAction(int virtualViewId, int action, Bundle arguments) {
         return false;
     }
 
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index 8182d29..9d7a928 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -16,6 +16,7 @@
 
 package android.view.accessibility;
 
+import android.os.Bundle;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 
@@ -47,7 +48,7 @@
         IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
         long interrogatingTid);
 
-    void performAccessibilityAction(long accessibilityNodeId, int action, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
-        long interrogatingTid);
+    void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments,
+        int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+        int interrogatingPid, long interrogatingTid);
 }
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 992849d..11d1ed0 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -26,6 +26,7 @@
 import android.graphics.Paint.Align;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.text.InputFilter;
 import android.text.InputType;
 import android.text.Spanned;
@@ -2068,7 +2069,7 @@
         }
 
         @Override
-        public boolean performAccessibilityAction(int action, int virtualViewId) {
+        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
             switch (virtualViewId) {
                 case VIRTUAL_VIEW_ID_INPUT: {
                     switch (action) {
@@ -2086,7 +2087,7 @@
                     }
                 } break;
             }
-            return super.performAccessibilityAction(action, virtualViewId);
+            return super.performAction(virtualViewId, action, arguments);
         }
 
         public void sendAccessibilityEventForVirtualView(int virtualViewId, int eventType) {
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 57a4b29..ac88669d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -865,6 +865,10 @@
   <java-symbol type="string" name="yesterday" />
   <java-symbol type="string" name="imei" />
   <java-symbol type="string" name="meid" />
+  <java-symbol type="string" name="granularity_label_character" />
+  <java-symbol type="string" name="granularity_label_word" />
+  <java-symbol type="string" name="granularity_label_link" />
+  <java-symbol type="string" name="granularity_label_line" />
 
   <java-symbol type="plurals" name="abbrev_in_num_days" />
   <java-symbol type="plurals" name="abbrev_in_num_hours" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 149a78c..04866b9 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2027,6 +2027,15 @@
     <!-- Label for ALT modifier key.  Must be short to fit on key! -->
     <string name="password_keyboard_label_alt_key">ALT</string>
 
+    <!-- Label for granularity to traverse the content on an AccessibilityNodeInfo by character. Only spoken to the user. [CHAR LIMIT=NONE] -->
+    <string name="granularity_label_character">character</string>
+    <!-- Label for granularity to traverse the content on an AccessibilityNodeInfo by word. Only spoken to the user. [CHAR LIMIT=NONE] -->
+    <string name="granularity_label_word">word</string>
+    <!-- Label for granularity to traverse the content on an AccessibilityNodeInfo by link. Only spoken to the user. [CHAR LIMIT=NONE] -->
+    <string name="granularity_label_link">link</string>
+    <!-- Label for granularity to traverse the content on an AccessibilityNodeInfo by line. Only spoken to the user. [CHAR LIMIT=NONE] -->
+    <string name="granularity_label_line">line</string>
+
     <!-- A format string for 12-hour time of day, just the hour, not the minute, with lower-case "am" or "pm" (example: "3pm"). -->
     <string name="hour_ampm">"<xliff:g id="hour" example="3">%-l</xliff:g><xliff:g id="ampm" example="pm">%P</xliff:g>"</string>
 
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 86efd17..648e4d5 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -41,6 +41,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -1547,7 +1548,7 @@
 
         @Override
         public boolean performAccessibilityAction(int accessibilityWindowId,
-                long accessibilityNodeId, int action, int interactionId,
+                long accessibilityNodeId, int action, Bundle arguments, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) {
             final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
             IAccessibilityInteractionConnection connection = null;
@@ -1568,8 +1569,8 @@
                 final int flags = (mIncludeNotImportantViews) ?
                         AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
                 final int interrogatingPid = Binder.getCallingPid();
-                connection.performAccessibilityAction(accessibilityNodeId, action, interactionId,
-                        callback, flags, interrogatingPid, interrogatingTid);
+                connection.performAccessibilityAction(accessibilityNodeId, action, arguments,
+                        interactionId, callback, flags, interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
                 if (DEBUG) {
                     Slog.e(LOG_TAG, "Error calling performAccessibilityAction()");
@@ -1698,7 +1699,9 @@
             | AccessibilityNodeInfo.ACTION_SELECT
             | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION
             | AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS
-            | AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+            | AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS
+            | AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY
+            | AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY;
 
         private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
             AccessibilityEvent.TYPE_VIEW_CLICKED