diff options
4 files changed, 246 insertions, 72 deletions
diff --git a/api/current.txt b/api/current.txt index 408d044a9d32..f2b35a8f6881 100644 --- a/api/current.txt +++ b/api/current.txt @@ -24930,6 +24930,7 @@ package android.view.accessibility { method public static java.lang.String eventTypeToString(int); method public long getEventTime(); method public int getEventType(); + method public int getGranularity(); method public java.lang.CharSequence getPackageName(); method public android.view.accessibility.AccessibilityRecord getRecord(int); method public int getRecordCount(); @@ -24939,6 +24940,7 @@ package android.view.accessibility { method public static android.view.accessibility.AccessibilityEvent obtain(); method public void setEventTime(long); method public void setEventType(int); + method public void setGranularity(int); method public void setPackageName(java.lang.CharSequence); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; @@ -24960,6 +24962,7 @@ package android.view.accessibility { field public static final int TYPE_VIEW_SELECTED = 4; // 0x4 field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10 field public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000 + field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY = 131072; // 0x20000 field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800 field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20 } @@ -25000,7 +25003,7 @@ package android.view.accessibility { method public int getChildCount(); method public java.lang.CharSequence getClassName(); method public java.lang.CharSequence getContentDescription(); - method public java.lang.CharSequence[] getGranularities(); + method public int getGranularities(); method public java.lang.CharSequence getPackageName(); method public android.view.accessibility.AccessibilityNodeInfo getParent(); method public java.lang.CharSequence getText(); @@ -25034,7 +25037,7 @@ package android.view.accessibility { 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 setGranularities(int); method public void setLongClickable(boolean); method public void setPackageName(java.lang.CharSequence); method public void setParent(android.view.View); @@ -25047,7 +25050,8 @@ package android.view.accessibility { 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 java.lang.String ACTION_ARGUMENT_GRANULARITY_INT = "ACTION_ARGUMENT_GRANULARITY_INT"; + field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING"; 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 @@ -25055,11 +25059,18 @@ package android.view.accessibility { 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_NEXT_HTML_ELEMENT = 1024; // 0x400 field public static final int ACTION_PREVIOUS_AT_GRANULARITY = 512; // 0x200 + field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800 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 + field public static final int GRANULARITY_CHARACTER = 1; // 0x1 + field public static final int GRANULARITY_LINE = 4; // 0x4 + field public static final int GRANULARITY_PAGE = 16; // 0x10 + field public static final int GRANULARITY_PARAGRAPH = 8; // 0x8 + field public static final int GRANULARITY_WORD = 2; // 0x2 } public abstract class AccessibilityNodeProvider { diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 6cb15785da42..6d1166e56d95 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -226,6 +226,23 @@ import java.util.List; * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> * </p> + * <b>View text traversed at granularity</b> - represents the event of traversing the + * text of a view at a given granularity. For example, moving to the next word.</br> + * <em>Type:</em> {@link #TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY} </br> + * <em>Properties:</em></br> + * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> + * <li>{@link #getSource()} - The source info (for registered clients).</li> + * <li>{@link #getClassName()} - The class name of the source.</li> + * <li>{@link #getPackageName()} - The package name of the source.</li> + * <li>{@link #getEventTime()} - The event time.</li> + * <li>{@link #getText()} - The text of the current text at the granularity.</li> + * <li>{@link #isPassword()} - Whether the source is password.</li> + * <li>{@link #isEnabled()} - Whether the source is enabled.</li> + * <li>{@link #getContentDescription()} - The content description of the source.</li> + * <li>{@link #getGranularity()} - Sets the granularity at which a view's text was traversed.</li> + * </ul> + * </p> * <p> * <b>View scrolled</b> - represents the event of scrolling a view. If * the source is a descendant of {@link android.widget.AdapterView} the @@ -580,6 +597,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000; /** + * Represents the event of traversing the text of a view at a given granularity. + */ + public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY = 0x00020000; + + /** * Mask for {@link AccessibilityEvent} all types. * * @see #TYPE_VIEW_CLICKED @@ -597,6 +619,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @see #TYPE_VIEW_SCROLLED * @see #TYPE_VIEW_TEXT_SELECTION_CHANGED * @see #TYPE_ANNOUNCEMENT + * @see #TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY */ public static final int TYPES_ALL_MASK = 0xFFFFFFFF; @@ -610,6 +633,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par private int mEventType; private CharSequence mPackageName; private long mEventTime; + int mGranularity; private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>(); @@ -627,6 +651,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par void init(AccessibilityEvent event) { super.init(event); mEventType = event.mEventType; + mGranularity = event.mGranularity; mEventTime = event.mEventTime; mPackageName = event.mPackageName; } @@ -744,6 +769,27 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** + * Sets the text granularity that was traversed. + * + * @param granularity The granularity. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setGranularity(int granularity) { + enforceNotSealed(); + mGranularity = granularity; + } + + /** + * Gets the text granularity that was traversed. + * + * @return The granularity. + */ + public int getGranularity() { + return mGranularity; + } + + /** * Returns a cached instance if such is available or a new one is * instantiated with its type property set. * @@ -831,6 +877,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par protected void clear() { super.clear(); mEventType = 0; + mGranularity = 0; mPackageName = null; mEventTime = 0; while (!mRecords.isEmpty()) { @@ -847,6 +894,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public void initFromParcel(Parcel parcel) { mSealed = (parcel.readInt() == 1); mEventType = parcel.readInt(); + mGranularity = parcel.readInt(); mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); mEventTime = parcel.readLong(); mConnectionId = parcel.readInt(); @@ -897,6 +945,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(isSealed() ? 1 : 0); parcel.writeInt(mEventType); + parcel.writeInt(mGranularity); TextUtils.writeToParcel(mPackageName, parcel, 0); parcel.writeLong(mEventTime); parcel.writeInt(mConnectionId); @@ -953,6 +1002,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par builder.append("EventType: ").append(eventTypeToString(mEventType)); builder.append("; EventTime: ").append(mEventTime); builder.append("; PackageName: ").append(mPackageName); + builder.append("; Granularity: ").append(mGranularity); builder.append(super.toString()); if (DEBUG) { builder.append("\n"); @@ -1033,6 +1083,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par return "TYPE_VIEW_ACCESSIBILITY_FOCUSED"; case TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: return "TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED"; + case TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY: + return "TYPE_CURRENT_AT_GRANULARITY_CHANGED"; default: return null; } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 3cb3b540952b..aec18fe4043e 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -102,7 +102,7 @@ public class AccessibilityNodeInfo implements Parcelable { public static final int ACTION_CLEAR_SELECTION = 0x00000008; /** - * Action that clicks on the node info. + * Action that long clicks on the node info. */ public static final int ACTION_CLICK = 0x00000010; @@ -122,78 +122,105 @@ public class AccessibilityNodeInfo implements Parcelable { 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> + * Action that requests to go to the next entity in this node's text + * at a given granularity. For example, move to the next character, word, etc. * <p> + * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_GRANULARITY_INT}<br> * <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); + * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY_INT, + * AccessibilityNodeInfo.GRANULARITY_CHARACTER); * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY, arguments); * </code></pre></p> - * </li> - * </ul> * </p> - * @see #setGranularities(CharSequence[]) + * + * @see #setGranularities(int) * @see #getGranularities() + * + * @see #GRANULARITY_CHARACTER + * @see #GRANULARITY_WORD + * @see #GRANULARITY_LINE + * @see #GRANULARITY_PARAGRAPH + * @see #GRANULARITY_PAGE */ 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> + * Action that requests to go to the previous entity in this node's text + * at a given granularity. For example, move to the next character, word, etc. * <p> + * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_GRANULARITY_INT}<br> * <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); + * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY_INT, + * AccessibilityNodeInfo.GRANULARITY_CHARACTER); + * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY, arguments); * </code></pre></p> - * </li> - * </ul> * </p> - * @see #setGranularities(CharSequence[]) + * + * @see #setGranularities(int) * @see #getGranularities() + * + * @see #GRANULARITY_CHARACTER + * @see #GRANULARITY_WORD + * @see #GRANULARITY_LINE + * @see #GRANULARITY_PARAGRAPH + * @see #GRANULARITY_PAGE */ public static final int ACTION_PREVIOUS_AT_GRANULARITY = 0x00000200; /** - * Argument for which content granularity to be used when traversing the node content. + * Action to move to the next HTML element of a given type. For example, move + * to the BUTTON, INPUT, TABLE, etc. * <p> - * <strong>Actions:</strong> - * <ul> - * <li> - * {@link #ACTION_PREVIOUS_AT_GRANULARITY} - * </li> - * <li> + * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> + * <strong>Example:</strong> + * <code><pre><p> + * Bundle arguments = new Bundle(); + * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); + * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments); + * </code></pre></p> + * </p> + */ + public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400; + + /** + * Action to move to the previous HTML element of a given type. For example, move + * to the BUTTON, INPUT, TABLE, etc. + * <p> + * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> + * <strong>Example:</strong> + * <code><pre><p> + * Bundle arguments = new Bundle(); + * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); + * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments); + * </code></pre></p> + * </p> + */ + public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800; + + /** + * Argument for which text granularity to be used when traversing the node text. + * <p> + * <strong>Type:</strong> int<br> + * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_GRANULARITY}, * {@link #ACTION_PREVIOUS_AT_GRANULARITY} - * </li> - * </ul> * </p> */ - public static final String ACTION_ARGUMENT_GRANULARITY = "ACTION_ARGUMENT_GRANULARITY"; + public static final String ACTION_ARGUMENT_GRANULARITY_INT = "ACTION_ARGUMENT_GRANULARITY_INT"; + + /** + * Argument for which HTML element to get moving to the next/previous HTML element. + * <p> + * <strong>Type:</strong> String<br> + * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT}, + * {@link #ACTION_PREVIOUS_HTML_ELEMENT} + * </p> + */ + public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = + "ACTION_ARGUMENT_HTML_ELEMENT_STRING"; /** * The input focus. @@ -205,6 +232,33 @@ public class AccessibilityNodeInfo implements Parcelable { */ public static final int FOCUS_ACCESSIBILITY = 2; + // Granularities + + /** + * Granularity bit for traversing the text of a node by character. + */ + public static final int GRANULARITY_CHARACTER = 0x00000001; + + /** + * Granularity bit for traversing the text of a node by word. + */ + public static final int GRANULARITY_WORD = 0x00000002; + + /** + * Granularity bit for traversing the text of a node by line. + */ + public static final int GRANULARITY_LINE = 0x00000004; + + /** + * Granularity bit for traversing the text of a node by paragraph. + */ + public static final int GRANULARITY_PARAGRAPH = 0x00000008; + + /** + * Granularity bit for traversing the text of a node by page. + */ + public static final int GRANULARITY_PAGE = 0x00000010; + // Boolean attributes. private static final int PROPERTY_CHECKABLE = 0x00000001; @@ -308,7 +362,7 @@ public class AccessibilityNodeInfo implements Parcelable { private final SparseLongArray mChildNodeIds = new SparseLongArray(); private int mActions; - private CharSequence[] mGranularities; + private int mGranularities; private int mConnectionId = UNDEFINED; @@ -532,28 +586,28 @@ public class AccessibilityNodeInfo implements Parcelable { } /** - * Sets the granularities for traversing the content of this node. + * Sets the text granularities for traversing the text 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. + * @param granularities The bit mask with granularities. * * @throws IllegalStateException If called from an AccessibilityService. */ - public void setGranularities(CharSequence[] granularities) { + public void setGranularities(int granularities) { enforceNotSealed(); mGranularities = granularities; } /** - * Gets the granularities for traversing the content of this node. + * Gets the granularities for traversing the text of this node. * - * @return The count. + * @return The bit mask with granularities. */ - public CharSequence[] getGranularities() { + public int getGranularities() { return mGranularities; } @@ -1339,8 +1393,6 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.writeLong(mParentNodeId); parcel.writeInt(mConnectionId); - parcel.writeCharSequenceArray(mGranularities); - SparseLongArray childIds = mChildNodeIds; final int childIdsSize = childIds.size(); parcel.writeInt(childIdsSize); @@ -1360,6 +1412,8 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.writeInt(mActions); + parcel.writeInt(mGranularities); + parcel.writeInt(mBooleanProperties); parcel.writeCharSequence(mPackageName); @@ -1392,7 +1446,7 @@ public class AccessibilityNodeInfo implements Parcelable { mContentDescription = other.mContentDescription; mActions= other.mActions; mBooleanProperties = other.mBooleanProperties; - mGranularities = (other.mGranularities) != null ? other.mGranularities.clone() : null; + mGranularities = other.mGranularities; final int otherChildIdCount = other.mChildNodeIds.size(); for (int i = 0; i < otherChildIdCount; i++) { mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i)); @@ -1411,8 +1465,6 @@ public class AccessibilityNodeInfo implements Parcelable { mParentNodeId = parcel.readLong(); mConnectionId = parcel.readInt(); - mGranularities = parcel.readCharSequenceArray(); - SparseLongArray childIds = mChildNodeIds; final int childrenSize = parcel.readInt(); for (int i = 0; i < childrenSize; i++) { @@ -1432,6 +1484,8 @@ public class AccessibilityNodeInfo implements Parcelable { mActions = parcel.readInt(); + mGranularities = parcel.readInt(); + mBooleanProperties = parcel.readInt(); mPackageName = parcel.readCharSequence(); @@ -1449,7 +1503,7 @@ public class AccessibilityNodeInfo implements Parcelable { mParentNodeId = ROOT_NODE_ID; mWindowId = UNDEFINED; mConnectionId = UNDEFINED; - mGranularities = null; + mGranularities = 0; mChildNodeIds.clear(); mBoundsInParent.set(0, 0, 0, 0); mBoundsInScreen.set(0, 0, 0, 0); @@ -1482,6 +1536,29 @@ public class AccessibilityNodeInfo implements Parcelable { } } + /** + * Gets the human readable granularity symbolic name. + * + * @param granularity The action. + * @return The symbolic name. + */ + private static String getGranularitySymbolicName(int granularity) { + switch (granularity) { + case GRANULARITY_CHARACTER: + return "GRANULARITY_CHARACTER"; + case GRANULARITY_WORD: + return "GRANULARITY_WORD"; + case GRANULARITY_LINE: + return "GRANULARITY_LINE"; + case GRANULARITY_PARAGRAPH: + return "GRANULARITY_PARAGRAPH"; + case GRANULARITY_PAGE: + return "GRANULARITY_PAGE"; + default: + throw new IllegalArgumentException("Unknown granularity: " + granularity); + } + } + private boolean canPerformRequestOverConnection(long accessibilityNodeId) { return (mWindowId != UNDEFINED && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED @@ -1529,11 +1606,13 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId)); builder.append("; mParentNodeId: " + mParentNodeId); - CharSequence[] granularities = mGranularities; + int granularities = mGranularities; builder.append("; granularities: ["); - for (int i = 0, count = granularities.length; i < count; i++) { - builder.append(granularities[i]); - if (i < count - 1) { + while (granularities != 0) { + final int granularity = 1 << Integer.numberOfTrailingZeros(granularities); + granularities &= ~granularity; + builder.append(getGranularitySymbolicName(granularity)); + if (granularities != 0) { builder.append(", "); } } @@ -1570,7 +1649,6 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("; scrollable: " + isScrollable()); builder.append("; ["); - for (int actionBits = mActions; actionBits != 0;) { final int action = 1 << Integer.numberOfTrailingZeros(actionBits); actionBits &= ~action; @@ -1579,7 +1657,6 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append(", "); } } - builder.append("]"); return builder.toString(); diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index b9404f363e3d..01ddf1fda257 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1554,7 +1554,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub IAccessibilityInteractionConnection connection = null; synchronized (mLock) { final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this, - resolvedWindowId, action); + resolvedWindowId, action, arguments); if (!permissionGranted) { return false; } else { @@ -1702,7 +1702,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub | AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS | AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS | AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY - | AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY; + | AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY + | AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT + | AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT; + + private static final int VALID_GRANULARITIES = + AccessibilityNodeInfo.GRANULARITY_CHARACTER + | AccessibilityNodeInfo.GRANULARITY_WORD + | AccessibilityNodeInfo.GRANULARITY_LINE + | AccessibilityNodeInfo.GRANULARITY_PARAGRAPH + | AccessibilityNodeInfo.GRANULARITY_PAGE; private static final int RETRIEVAL_ALLOWING_EVENT_TYPES = AccessibilityEvent.TYPE_VIEW_CLICKED @@ -1752,10 +1761,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId); } - public boolean canPerformActionLocked(Service service, int windowId, int action) { + public boolean canPerformActionLocked(Service service, int windowId, int action, + Bundle arguments) { return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId) - && isActionPermitted(action); + && isActionPermitted(action) + && isActionArgumentsValid(action, arguments); } public boolean canRetrieveWindowContent(Service service) { @@ -1779,6 +1790,29 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return (VALID_ACTIONS & action) != 0; } + private boolean isActionArgumentsValid(int action, Bundle arguments) { + switch (action) { + case AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY: + case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY: { + if (arguments.size() == 1) { + final int granularity = arguments.getInt( + AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY_INT); + return (granularity & VALID_GRANULARITIES) != 0 + && Integer.bitCount(granularity) == 1; + } + } break; + case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT: + case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT: { + if (arguments.size() == 1) { + String element = arguments.getString( + AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING); + return !TextUtils.isEmpty(element); + } + } break; + } + return false; + } + private void enforceCallingPermission(String permission, String function) { if (OWN_PROCESS_ID == Binder.getCallingPid()) { return; |