Home | History | Annotate | Download | only in accessibility
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.view.accessibility;
     18 
     19 import android.graphics.Rect;
     20 import android.os.Bundle;
     21 import android.os.Parcel;
     22 import android.os.Parcelable;
     23 import android.util.SparseLongArray;
     24 import android.view.View;
     25 
     26 import java.util.Collections;
     27 import java.util.List;
     28 
     29 /**
     30  * This class represents a node of the window content as well as actions that
     31  * can be requested from its source. From the point of view of an
     32  * {@link android.accessibilityservice.AccessibilityService} a window content is
     33  * presented as tree of accessibility node info which may or may not map one-to-one
     34  * to the view hierarchy. In other words, a custom view is free to report itself as
     35  * a tree of accessibility node info.
     36  * </p>
     37  * <p>
     38  * Once an accessibility node info is delivered to an accessibility service it is
     39  * made immutable and calling a state mutation method generates an error.
     40  * </p>
     41  * <p>
     42  * Please refer to {@link android.accessibilityservice.AccessibilityService} for
     43  * details about how to obtain a handle to window content as a tree of accessibility
     44  * node info as well as familiarizing with the security model.
     45  * </p>
     46  * <div class="special reference">
     47  * <h3>Developer Guides</h3>
     48  * <p>For more information about making applications accessible, read the
     49  * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
     50  * developer guide.</p>
     51  * </div>
     52  *
     53  * @see android.accessibilityservice.AccessibilityService
     54  * @see AccessibilityEvent
     55  * @see AccessibilityManager
     56  */
     57 public class AccessibilityNodeInfo implements Parcelable {
     58 
     59     private static final boolean DEBUG = false;
     60 
     61     /** @hide */
     62     public static final int UNDEFINED = -1;
     63 
     64     /** @hide */
     65     public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED);
     66 
     67     /** @hide */
     68     public static final int ACTIVE_WINDOW_ID = UNDEFINED;
     69 
     70     /** @hide */
     71     public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
     72 
     73     /** @hide */
     74     public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
     75 
     76     /** @hide */
     77     public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004;
     78 
     79     /** @hide */
     80     public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008;
     81 
     82     // Actions.
     83 
     84     /**
     85      * Action that gives input focus to the node.
     86      */
     87     public static final int ACTION_FOCUS =  0x00000001;
     88 
     89     /**
     90      * Action that clears input focus of the node.
     91      */
     92     public static final int ACTION_CLEAR_FOCUS = 0x00000002;
     93 
     94     /**
     95      * Action that selects the node.
     96      */
     97     public static final int ACTION_SELECT = 0x00000004;
     98 
     99     /**
    100      * Action that unselects the node.
    101      */
    102     public static final int ACTION_CLEAR_SELECTION = 0x00000008;
    103 
    104     /**
    105      * Action that clicks on the node info.
    106      */
    107     public static final int ACTION_CLICK = 0x00000010;
    108 
    109     /**
    110      * Action that long clicks on the node.
    111      */
    112     public static final int ACTION_LONG_CLICK = 0x00000020;
    113 
    114     /**
    115      * Action that gives accessibility focus to the node.
    116      */
    117     public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;
    118 
    119     /**
    120      * Action that clears accessibility focus of the node.
    121      */
    122     public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
    123 
    124     /**
    125      * Action that requests to go to the next entity in this node's text
    126      * at a given movement granularity. For example, move to the next character,
    127      * word, etc.
    128      * <p>
    129      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br>
    130      * <strong>Example:</strong>
    131      * <code><pre><p>
    132      *   Bundle arguments = new Bundle();
    133      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
    134      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
    135      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
    136      * </code></pre></p>
    137      * </p>
    138      *
    139      * @see #setMovementGranularities(int)
    140      * @see #getMovementGranularities()
    141      *
    142      * @see #MOVEMENT_GRANULARITY_CHARACTER
    143      * @see #MOVEMENT_GRANULARITY_WORD
    144      * @see #MOVEMENT_GRANULARITY_LINE
    145      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
    146      * @see #MOVEMENT_GRANULARITY_PAGE
    147      */
    148     public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;
    149 
    150     /**
    151      * Action that requests to go to the previous entity in this node's text
    152      * at a given movement granularity. For example, move to the next character,
    153      * word, etc.
    154      * <p>
    155      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br>
    156      * <strong>Example:</strong>
    157      * <code><pre><p>
    158      *   Bundle arguments = new Bundle();
    159      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
    160      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
    161      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
    162      *           arguments);
    163      * </code></pre></p>
    164      * </p>
    165      *
    166      * @see #setMovementGranularities(int)
    167      * @see #getMovementGranularities()
    168      *
    169      * @see #MOVEMENT_GRANULARITY_CHARACTER
    170      * @see #MOVEMENT_GRANULARITY_WORD
    171      * @see #MOVEMENT_GRANULARITY_LINE
    172      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
    173      * @see #MOVEMENT_GRANULARITY_PAGE
    174      */
    175     public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;
    176 
    177     /**
    178      * Action to move to the next HTML element of a given type. For example, move
    179      * to the BUTTON, INPUT, TABLE, etc.
    180      * <p>
    181      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
    182      * <strong>Example:</strong>
    183      * <code><pre><p>
    184      *   Bundle arguments = new Bundle();
    185      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
    186      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
    187      * </code></pre></p>
    188      * </p>
    189      */
    190     public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
    191 
    192     /**
    193      * Action to move to the previous HTML element of a given type. For example, move
    194      * to the BUTTON, INPUT, TABLE, etc.
    195      * <p>
    196      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
    197      * <strong>Example:</strong>
    198      * <code><pre><p>
    199      *   Bundle arguments = new Bundle();
    200      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
    201      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
    202      * </code></pre></p>
    203      * </p>
    204      */
    205     public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
    206 
    207     /**
    208      * Action to scroll the node content forward.
    209      */
    210     public static final int ACTION_SCROLL_FORWARD = 0x00001000;
    211 
    212     /**
    213      * Action to scroll the node content backward.
    214      */
    215     public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
    216 
    217     /**
    218      * Argument for which movement granularity to be used when traversing the node text.
    219      * <p>
    220      * <strong>Type:</strong> int<br>
    221      * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
    222      * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
    223      * </p>
    224      */
    225     public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
    226         "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
    227 
    228     /**
    229      * Argument for which HTML element to get moving to the next/previous HTML element.
    230      * <p>
    231      * <strong>Type:</strong> String<br>
    232      * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
    233      *         {@link #ACTION_PREVIOUS_HTML_ELEMENT}
    234      * </p>
    235      */
    236     public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
    237         "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
    238 
    239     /**
    240      * The input focus.
    241      */
    242     public static final int FOCUS_INPUT = 1;
    243 
    244     /**
    245      * The accessibility focus.
    246      */
    247     public static final int FOCUS_ACCESSIBILITY = 2;
    248 
    249     // Movement granularities
    250 
    251     /**
    252      * Movement granularity bit for traversing the text of a node by character.
    253      */
    254     public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001;
    255 
    256     /**
    257      * Movement granularity bit for traversing the text of a node by word.
    258      */
    259     public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002;
    260 
    261     /**
    262      * Movement granularity bit for traversing the text of a node by line.
    263      */
    264     public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004;
    265 
    266     /**
    267      * Movement granularity bit for traversing the text of a node by paragraph.
    268      */
    269     public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008;
    270 
    271     /**
    272      * Movement granularity bit for traversing the text of a node by page.
    273      */
    274     public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;
    275 
    276     // Boolean attributes.
    277 
    278     private static final int PROPERTY_CHECKABLE = 0x00000001;
    279 
    280     private static final int PROPERTY_CHECKED = 0x00000002;
    281 
    282     private static final int PROPERTY_FOCUSABLE = 0x00000004;
    283 
    284     private static final int PROPERTY_FOCUSED = 0x00000008;
    285 
    286     private static final int PROPERTY_SELECTED = 0x00000010;
    287 
    288     private static final int PROPERTY_CLICKABLE = 0x00000020;
    289 
    290     private static final int PROPERTY_LONG_CLICKABLE = 0x00000040;
    291 
    292     private static final int PROPERTY_ENABLED = 0x00000080;
    293 
    294     private static final int PROPERTY_PASSWORD = 0x00000100;
    295 
    296     private static final int PROPERTY_SCROLLABLE = 0x00000200;
    297 
    298     private static final int PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
    299 
    300     private static final int PROPERTY_VISIBLE_TO_USER = 0x00000800;
    301 
    302     /**
    303      * Bits that provide the id of a virtual descendant of a view.
    304      */
    305     private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L;
    306 
    307     /**
    308      * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a
    309      * virtual descendant of a view. Such a descendant does not exist in the view
    310      * hierarchy and is only reported via the accessibility APIs.
    311      */
    312     private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
    313 
    314     /**
    315      * Gets the accessibility view id which identifies a View in the view three.
    316      *
    317      * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
    318      * @return The accessibility view id part of the node id.
    319      *
    320      * @hide
    321      */
    322     public static int getAccessibilityViewId(long accessibilityNodeId) {
    323         return (int) accessibilityNodeId;
    324     }
    325 
    326     /**
    327      * Gets the virtual descendant id which identifies an imaginary view in a
    328      * containing View.
    329      *
    330      * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
    331      * @return The virtual view id part of the node id.
    332      *
    333      * @hide
    334      */
    335     public static int getVirtualDescendantId(long accessibilityNodeId) {
    336         return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK)
    337                 >> VIRTUAL_DESCENDANT_ID_SHIFT);
    338     }
    339 
    340     /**
    341      * Makes a node id by shifting the <code>virtualDescendantId</code>
    342      * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking
    343      * the bitwise or with the <code>accessibilityViewId</code>.
    344      *
    345      * @param accessibilityViewId A View accessibility id.
    346      * @param virtualDescendantId A virtual descendant id.
    347      * @return The node id.
    348      *
    349      * @hide
    350      */
    351     public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) {
    352         return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
    353     }
    354 
    355     // Housekeeping.
    356     private static final int MAX_POOL_SIZE = 50;
    357     private static final Object sPoolLock = new Object();
    358     private static AccessibilityNodeInfo sPool;
    359     private static int sPoolSize;
    360     private AccessibilityNodeInfo mNext;
    361     private boolean mIsInPool;
    362     private boolean mSealed;
    363 
    364     // Data.
    365     private int mWindowId = UNDEFINED;
    366     private long mSourceNodeId = ROOT_NODE_ID;
    367     private long mParentNodeId = ROOT_NODE_ID;
    368     private long mLabelForId = ROOT_NODE_ID;
    369     private long mLabeledById = ROOT_NODE_ID;
    370 
    371     private int mBooleanProperties;
    372     private final Rect mBoundsInParent = new Rect();
    373     private final Rect mBoundsInScreen = new Rect();
    374 
    375     private CharSequence mPackageName;
    376     private CharSequence mClassName;
    377     private CharSequence mText;
    378     private CharSequence mContentDescription;
    379 
    380     private final SparseLongArray mChildNodeIds = new SparseLongArray();
    381     private int mActions;
    382 
    383     private int mMovementGranularities;
    384 
    385     private int mConnectionId = UNDEFINED;
    386 
    387     /**
    388      * Hide constructor from clients.
    389      */
    390     private AccessibilityNodeInfo() {
    391         /* do nothing */
    392     }
    393 
    394     /**
    395      * Sets the source.
    396      * <p>
    397      *   <strong>Note:</strong> Cannot be called from an
    398      *   {@link android.accessibilityservice.AccessibilityService}.
    399      *   This class is made immutable before being delivered to an AccessibilityService.
    400      * </p>
    401      *
    402      * @param source The info source.
    403      */
    404     public void setSource(View source) {
    405         setSource(source, UNDEFINED);
    406     }
    407 
    408     /**
    409      * Sets the source to be a virtual descendant of the given <code>root</code>.
    410      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
    411      * is set as the source.
    412      * <p>
    413      * A virtual descendant is an imaginary View that is reported as a part of the view
    414      * hierarchy for accessibility purposes. This enables custom views that draw complex
    415      * content to report themselves as a tree of virtual views, thus conveying their
    416      * logical structure.
    417      * </p>
    418      * <p>
    419      *   <strong>Note:</strong> Cannot be called from an
    420      *   {@link android.accessibilityservice.AccessibilityService}.
    421      *   This class is made immutable before being delivered to an AccessibilityService.
    422      * </p>
    423      *
    424      * @param root The root of the virtual subtree.
    425      * @param virtualDescendantId The id of the virtual descendant.
    426      */
    427     public void setSource(View root, int virtualDescendantId) {
    428         enforceNotSealed();
    429         mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
    430         final int rootAccessibilityViewId =
    431             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
    432         mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
    433     }
    434 
    435     /**
    436      * Find the view that has the specified focus type. The search starts from
    437      * the view represented by this node info.
    438      *
    439      * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
    440      *         {@link #FOCUS_ACCESSIBILITY}.
    441      * @return The node info of the focused view or null.
    442      *
    443      * @see #FOCUS_INPUT
    444      * @see #FOCUS_ACCESSIBILITY
    445      */
    446     public AccessibilityNodeInfo findFocus(int focus) {
    447         enforceSealed();
    448         enforceValidFocusType(focus);
    449         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    450             return null;
    451         }
    452         return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId,
    453                 mSourceNodeId, focus);
    454     }
    455 
    456     /**
    457      * Searches for the nearest view in the specified direction that can take
    458      * the input focus.
    459      *
    460      * @param direction The direction. Can be one of:
    461      *     {@link View#FOCUS_DOWN},
    462      *     {@link View#FOCUS_UP},
    463      *     {@link View#FOCUS_LEFT},
    464      *     {@link View#FOCUS_RIGHT},
    465      *     {@link View#FOCUS_FORWARD},
    466      *     {@link View#FOCUS_BACKWARD}.
    467      *
    468      * @return The node info for the view that can take accessibility focus.
    469      */
    470     public AccessibilityNodeInfo focusSearch(int direction) {
    471         enforceSealed();
    472         enforceValidFocusDirection(direction);
    473         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    474             return null;
    475         }
    476         return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId,
    477                 mSourceNodeId, direction);
    478     }
    479 
    480     /**
    481      * Gets the id of the window from which the info comes from.
    482      *
    483      * @return The window id.
    484      */
    485     public int getWindowId() {
    486         return mWindowId;
    487     }
    488 
    489     /**
    490      * @return The ids of the children.
    491      *
    492      * @hide
    493      */
    494     public SparseLongArray getChildNodeIds() {
    495         return mChildNodeIds;
    496     }
    497 
    498     /**
    499      * Gets the number of children.
    500      *
    501      * @return The child count.
    502      */
    503     public int getChildCount() {
    504         return mChildNodeIds.size();
    505     }
    506 
    507     /**
    508      * Get the child at given index.
    509      * <p>
    510      *   <strong>Note:</strong> It is a client responsibility to recycle the
    511      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
    512      *     to avoid creating of multiple instances.
    513      * </p>
    514      *
    515      * @param index The child index.
    516      * @return The child node.
    517      *
    518      * @throws IllegalStateException If called outside of an AccessibilityService.
    519      *
    520      */
    521     public AccessibilityNodeInfo getChild(int index) {
    522         enforceSealed();
    523         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    524             return null;
    525         }
    526         final long childId = mChildNodeIds.get(index);
    527         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    528         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
    529                 childId, FLAG_PREFETCH_DESCENDANTS);
    530     }
    531 
    532     /**
    533      * Adds a child.
    534      * <p>
    535      * <strong>Note:</strong> Cannot be called from an
    536      * {@link android.accessibilityservice.AccessibilityService}.
    537      * This class is made immutable before being delivered to an AccessibilityService.
    538      * </p>
    539      *
    540      * @param child The child.
    541      *
    542      * @throws IllegalStateException If called from an AccessibilityService.
    543      */
    544     public void addChild(View child) {
    545         addChild(child, UNDEFINED);
    546     }
    547 
    548     /**
    549      * Adds a virtual child which is a descendant of the given <code>root</code>.
    550      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
    551      * is added as a child.
    552      * <p>
    553      * A virtual descendant is an imaginary View that is reported as a part of the view
    554      * hierarchy for accessibility purposes. This enables custom views that draw complex
    555      * content to report them selves as a tree of virtual views, thus conveying their
    556      * logical structure.
    557      * </p>
    558      *
    559      * @param root The root of the virtual subtree.
    560      * @param virtualDescendantId The id of the virtual child.
    561      */
    562     public void addChild(View root, int virtualDescendantId) {
    563         enforceNotSealed();
    564         final int index = mChildNodeIds.size();
    565         final int rootAccessibilityViewId =
    566             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
    567         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
    568         mChildNodeIds.put(index, childNodeId);
    569     }
    570 
    571     /**
    572      * Gets the actions that can be performed on the node.
    573      *
    574      * @return The bit mask of with actions.
    575      *
    576      * @see AccessibilityNodeInfo#ACTION_FOCUS
    577      * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
    578      * @see AccessibilityNodeInfo#ACTION_SELECT
    579      * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
    580      * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS
    581      * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS
    582      * @see AccessibilityNodeInfo#ACTION_CLICK
    583      * @see AccessibilityNodeInfo#ACTION_LONG_CLICK
    584      * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
    585      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
    586      * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT
    587      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT
    588      * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD
    589      * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD
    590      */
    591     public int getActions() {
    592         return mActions;
    593     }
    594 
    595     /**
    596      * Adds an action that can be performed on the node.
    597      * <p>
    598      *   <strong>Note:</strong> Cannot be called from an
    599      *   {@link android.accessibilityservice.AccessibilityService}.
    600      *   This class is made immutable before being delivered to an AccessibilityService.
    601      * </p>
    602      *
    603      * @param action The action.
    604      *
    605      * @throws IllegalStateException If called from an AccessibilityService.
    606      */
    607     public void addAction(int action) {
    608         enforceNotSealed();
    609         mActions |= action;
    610     }
    611 
    612     /**
    613      * Sets the movement granularities for traversing the text of this node.
    614      * <p>
    615      *   <strong>Note:</strong> Cannot be called from an
    616      *   {@link android.accessibilityservice.AccessibilityService}.
    617      *   This class is made immutable before being delivered to an AccessibilityService.
    618      * </p>
    619      *
    620      * @param granularities The bit mask with granularities.
    621      *
    622      * @throws IllegalStateException If called from an AccessibilityService.
    623      */
    624     public void setMovementGranularities(int granularities) {
    625         enforceNotSealed();
    626         mMovementGranularities = granularities;
    627     }
    628 
    629     /**
    630      * Gets the movement granularities for traversing the text of this node.
    631      *
    632      * @return The bit mask with granularities.
    633      */
    634     public int getMovementGranularities() {
    635         return mMovementGranularities;
    636     }
    637 
    638     /**
    639      * Performs an action on the node.
    640      * <p>
    641      *   <strong>Note:</strong> An action can be performed only if the request is made
    642      *   from an {@link android.accessibilityservice.AccessibilityService}.
    643      * </p>
    644      *
    645      * @param action The action to perform.
    646      * @return True if the action was performed.
    647      *
    648      * @throws IllegalStateException If called outside of an AccessibilityService.
    649      */
    650     public boolean performAction(int action) {
    651         enforceSealed();
    652         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    653             return false;
    654         }
    655         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    656         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
    657                 action, null);
    658     }
    659 
    660     /**
    661      * Performs an action on the node.
    662      * <p>
    663      *   <strong>Note:</strong> An action can be performed only if the request is made
    664      *   from an {@link android.accessibilityservice.AccessibilityService}.
    665      * </p>
    666      *
    667      * @param action The action to perform.
    668      * @param arguments A bundle with additional arguments.
    669      * @return True if the action was performed.
    670      *
    671      * @throws IllegalStateException If called outside of an AccessibilityService.
    672      */
    673     public boolean performAction(int action, Bundle arguments) {
    674         enforceSealed();
    675         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    676             return false;
    677         }
    678         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    679         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
    680                 action, arguments);
    681     }
    682 
    683     /**
    684      * Finds {@link AccessibilityNodeInfo}s by text. The match is case
    685      * insensitive containment. The search is relative to this info i.e.
    686      * this info is the root of the traversed tree.
    687      *
    688      * <p>
    689      *   <strong>Note:</strong> It is a client responsibility to recycle the
    690      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
    691      *     to avoid creating of multiple instances.
    692      * </p>
    693      *
    694      * @param text The searched text.
    695      * @return A list of node info.
    696      */
    697     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
    698         enforceSealed();
    699         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    700             return Collections.emptyList();
    701         }
    702         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    703         return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
    704                 text);
    705     }
    706 
    707     /**
    708      * Gets the parent.
    709      * <p>
    710      *   <strong>Note:</strong> It is a client responsibility to recycle the
    711      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
    712      *     to avoid creating of multiple instances.
    713      * </p>
    714      *
    715      * @return The parent.
    716      */
    717     public AccessibilityNodeInfo getParent() {
    718         enforceSealed();
    719         if (!canPerformRequestOverConnection(mParentNodeId)) {
    720             return null;
    721         }
    722         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    723         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
    724                 mWindowId, mParentNodeId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
    725     }
    726 
    727     /**
    728      * @return The parent node id.
    729      *
    730      * @hide
    731      */
    732     public long getParentNodeId() {
    733         return mParentNodeId;
    734     }
    735 
    736     /**
    737      * Sets the parent.
    738      * <p>
    739      *   <strong>Note:</strong> Cannot be called from an
    740      *   {@link android.accessibilityservice.AccessibilityService}.
    741      *   This class is made immutable before being delivered to an AccessibilityService.
    742      * </p>
    743      *
    744      * @param parent The parent.
    745      *
    746      * @throws IllegalStateException If called from an AccessibilityService.
    747      */
    748     public void setParent(View parent) {
    749         setParent(parent, UNDEFINED);
    750     }
    751 
    752     /**
    753      * Sets the parent to be a virtual descendant of the given <code>root</code>.
    754      * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
    755      * is set as the parent.
    756      * <p>
    757      * A virtual descendant is an imaginary View that is reported as a part of the view
    758      * hierarchy for accessibility purposes. This enables custom views that draw complex
    759      * content to report them selves as a tree of virtual views, thus conveying their
    760      * logical structure.
    761      * </p>
    762      * <p>
    763      *   <strong>Note:</strong> Cannot be called from an
    764      *   {@link android.accessibilityservice.AccessibilityService}.
    765      *   This class is made immutable before being delivered to an AccessibilityService.
    766      * </p>
    767      *
    768      * @param root The root of the virtual subtree.
    769      * @param virtualDescendantId The id of the virtual descendant.
    770      */
    771     public void setParent(View root, int virtualDescendantId) {
    772         enforceNotSealed();
    773         final int rootAccessibilityViewId =
    774             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
    775         mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
    776     }
    777 
    778     /**
    779      * Gets the node bounds in parent coordinates.
    780      *
    781      * @param outBounds The output node bounds.
    782      */
    783     public void getBoundsInParent(Rect outBounds) {
    784         outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
    785                 mBoundsInParent.right, mBoundsInParent.bottom);
    786     }
    787 
    788     /**
    789      * Sets the node bounds in parent coordinates.
    790      * <p>
    791      *   <strong>Note:</strong> Cannot be called from an
    792      *   {@link android.accessibilityservice.AccessibilityService}.
    793      *   This class is made immutable before being delivered to an AccessibilityService.
    794      * </p>
    795      *
    796      * @param bounds The node bounds.
    797      *
    798      * @throws IllegalStateException If called from an AccessibilityService.
    799      */
    800     public void setBoundsInParent(Rect bounds) {
    801         enforceNotSealed();
    802         mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
    803     }
    804 
    805     /**
    806      * Gets the node bounds in screen coordinates.
    807      *
    808      * @param outBounds The output node bounds.
    809      */
    810     public void getBoundsInScreen(Rect outBounds) {
    811         outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top,
    812                 mBoundsInScreen.right, mBoundsInScreen.bottom);
    813     }
    814 
    815     /**
    816      * Sets the node bounds in screen coordinates.
    817      * <p>
    818      *   <strong>Note:</strong> Cannot be called from an
    819      *   {@link android.accessibilityservice.AccessibilityService}.
    820      *   This class is made immutable before being delivered to an AccessibilityService.
    821      * </p>
    822      *
    823      * @param bounds The node bounds.
    824      *
    825      * @throws IllegalStateException If called from an AccessibilityService.
    826      */
    827     public void setBoundsInScreen(Rect bounds) {
    828         enforceNotSealed();
    829         mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
    830     }
    831 
    832     /**
    833      * Gets whether this node is checkable.
    834      *
    835      * @return True if the node is checkable.
    836      */
    837     public boolean isCheckable() {
    838         return getBooleanProperty(PROPERTY_CHECKABLE);
    839     }
    840 
    841     /**
    842      * Sets whether this node is checkable.
    843      * <p>
    844      *   <strong>Note:</strong> Cannot be called from an
    845      *   {@link android.accessibilityservice.AccessibilityService}.
    846      *   This class is made immutable before being delivered to an AccessibilityService.
    847      * </p>
    848      *
    849      * @param checkable True if the node is checkable.
    850      *
    851      * @throws IllegalStateException If called from an AccessibilityService.
    852      */
    853     public void setCheckable(boolean checkable) {
    854         setBooleanProperty(PROPERTY_CHECKABLE, checkable);
    855     }
    856 
    857     /**
    858      * Gets whether this node is checked.
    859      *
    860      * @return True if the node is checked.
    861      */
    862     public boolean isChecked() {
    863         return getBooleanProperty(PROPERTY_CHECKED);
    864     }
    865 
    866     /**
    867      * Sets whether this node is checked.
    868      * <p>
    869      *   <strong>Note:</strong> Cannot be called from an
    870      *   {@link android.accessibilityservice.AccessibilityService}.
    871      *   This class is made immutable before being delivered to an AccessibilityService.
    872      * </p>
    873      *
    874      * @param checked True if the node is checked.
    875      *
    876      * @throws IllegalStateException If called from an AccessibilityService.
    877      */
    878     public void setChecked(boolean checked) {
    879         setBooleanProperty(PROPERTY_CHECKED, checked);
    880     }
    881 
    882     /**
    883      * Gets whether this node is focusable.
    884      *
    885      * @return True if the node is focusable.
    886      */
    887     public boolean isFocusable() {
    888         return getBooleanProperty(PROPERTY_FOCUSABLE);
    889     }
    890 
    891     /**
    892      * Sets whether this node is focusable.
    893      * <p>
    894      *   <strong>Note:</strong> Cannot be called from an
    895      *   {@link android.accessibilityservice.AccessibilityService}.
    896      *   This class is made immutable before being delivered to an AccessibilityService.
    897      * </p>
    898      *
    899      * @param focusable True if the node is focusable.
    900      *
    901      * @throws IllegalStateException If called from an AccessibilityService.
    902      */
    903     public void setFocusable(boolean focusable) {
    904         setBooleanProperty(PROPERTY_FOCUSABLE, focusable);
    905     }
    906 
    907     /**
    908      * Gets whether this node is focused.
    909      *
    910      * @return True if the node is focused.
    911      */
    912     public boolean isFocused() {
    913         return getBooleanProperty(PROPERTY_FOCUSED);
    914     }
    915 
    916     /**
    917      * Sets whether this node is focused.
    918      * <p>
    919      *   <strong>Note:</strong> Cannot be called from an
    920      *   {@link android.accessibilityservice.AccessibilityService}.
    921      *   This class is made immutable before being delivered to an AccessibilityService.
    922      * </p>
    923      *
    924      * @param focused True if the node is focused.
    925      *
    926      * @throws IllegalStateException If called from an AccessibilityService.
    927      */
    928     public void setFocused(boolean focused) {
    929         setBooleanProperty(PROPERTY_FOCUSED, focused);
    930     }
    931 
    932     /**
    933      * Sets whether this node is visible to the user.
    934      *
    935      * @return Whether the node is visible to the user.
    936      */
    937     public boolean isVisibleToUser() {
    938         return getBooleanProperty(PROPERTY_VISIBLE_TO_USER);
    939     }
    940 
    941     /**
    942      * Sets whether this node is visible to the user.
    943      * <p>
    944      *   <strong>Note:</strong> Cannot be called from an
    945      *   {@link android.accessibilityservice.AccessibilityService}.
    946      *   This class is made immutable before being delivered to an AccessibilityService.
    947      * </p>
    948      *
    949      * @param visibleToUser Whether the node is visible to the user.
    950      *
    951      * @throws IllegalStateException If called from an AccessibilityService.
    952      */
    953     public void setVisibleToUser(boolean visibleToUser) {
    954         setBooleanProperty(PROPERTY_VISIBLE_TO_USER, visibleToUser);
    955     }
    956 
    957     /**
    958      * Gets whether this node is accessibility focused.
    959      *
    960      * @return True if the node is accessibility focused.
    961      */
    962     public boolean isAccessibilityFocused() {
    963         return getBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED);
    964     }
    965 
    966     /**
    967      * Sets whether this node is accessibility focused.
    968      * <p>
    969      *   <strong>Note:</strong> Cannot be called from an
    970      *   {@link android.accessibilityservice.AccessibilityService}.
    971      *   This class is made immutable before being delivered to an AccessibilityService.
    972      * </p>
    973      *
    974      * @param focused True if the node is accessibility focused.
    975      *
    976      * @throws IllegalStateException If called from an AccessibilityService.
    977      */
    978     public void setAccessibilityFocused(boolean focused) {
    979         setBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED, focused);
    980     }
    981 
    982     /**
    983      * Gets whether this node is selected.
    984      *
    985      * @return True if the node is selected.
    986      */
    987     public boolean isSelected() {
    988         return getBooleanProperty(PROPERTY_SELECTED);
    989     }
    990 
    991     /**
    992      * Sets whether this node is selected.
    993      * <p>
    994      *   <strong>Note:</strong> Cannot be called from an
    995      *   {@link android.accessibilityservice.AccessibilityService}.
    996      *   This class is made immutable before being delivered to an AccessibilityService.
    997      * </p>
    998      *
    999      * @param selected True if the node is selected.
   1000      *
   1001      * @throws IllegalStateException If called from an AccessibilityService.
   1002      */
   1003     public void setSelected(boolean selected) {
   1004         setBooleanProperty(PROPERTY_SELECTED, selected);
   1005     }
   1006 
   1007     /**
   1008      * Gets whether this node is clickable.
   1009      *
   1010      * @return True if the node is clickable.
   1011      */
   1012     public boolean isClickable() {
   1013         return getBooleanProperty(PROPERTY_CLICKABLE);
   1014     }
   1015 
   1016     /**
   1017      * Sets whether this node is clickable.
   1018      * <p>
   1019      *   <strong>Note:</strong> Cannot be called from an
   1020      *   {@link android.accessibilityservice.AccessibilityService}.
   1021      *   This class is made immutable before being delivered to an AccessibilityService.
   1022      * </p>
   1023      *
   1024      * @param clickable True if the node is clickable.
   1025      *
   1026      * @throws IllegalStateException If called from an AccessibilityService.
   1027      */
   1028     public void setClickable(boolean clickable) {
   1029         setBooleanProperty(PROPERTY_CLICKABLE, clickable);
   1030     }
   1031 
   1032     /**
   1033      * Gets whether this node is long clickable.
   1034      *
   1035      * @return True if the node is long clickable.
   1036      */
   1037     public boolean isLongClickable() {
   1038         return getBooleanProperty(PROPERTY_LONG_CLICKABLE);
   1039     }
   1040 
   1041     /**
   1042      * Sets whether this node is long clickable.
   1043      * <p>
   1044      *   <strong>Note:</strong> Cannot be called from an
   1045      *   {@link android.accessibilityservice.AccessibilityService}.
   1046      *   This class is made immutable before being delivered to an AccessibilityService.
   1047      * </p>
   1048      *
   1049      * @param longClickable True if the node is long clickable.
   1050      *
   1051      * @throws IllegalStateException If called from an AccessibilityService.
   1052      */
   1053     public void setLongClickable(boolean longClickable) {
   1054         setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable);
   1055     }
   1056 
   1057     /**
   1058      * Gets whether this node is enabled.
   1059      *
   1060      * @return True if the node is enabled.
   1061      */
   1062     public boolean isEnabled() {
   1063         return getBooleanProperty(PROPERTY_ENABLED);
   1064     }
   1065 
   1066     /**
   1067      * Sets whether this node is enabled.
   1068      * <p>
   1069      *   <strong>Note:</strong> Cannot be called from an
   1070      *   {@link android.accessibilityservice.AccessibilityService}.
   1071      *   This class is made immutable before being delivered to an AccessibilityService.
   1072      * </p>
   1073      *
   1074      * @param enabled True if the node is enabled.
   1075      *
   1076      * @throws IllegalStateException If called from an AccessibilityService.
   1077      */
   1078     public void setEnabled(boolean enabled) {
   1079         setBooleanProperty(PROPERTY_ENABLED, enabled);
   1080     }
   1081 
   1082     /**
   1083      * Gets whether this node is a password.
   1084      *
   1085      * @return True if the node is a password.
   1086      */
   1087     public boolean isPassword() {
   1088         return getBooleanProperty(PROPERTY_PASSWORD);
   1089     }
   1090 
   1091     /**
   1092      * Sets whether this node is a password.
   1093      * <p>
   1094      *   <strong>Note:</strong> Cannot be called from an
   1095      *   {@link android.accessibilityservice.AccessibilityService}.
   1096      *   This class is made immutable before being delivered to an AccessibilityService.
   1097      * </p>
   1098      *
   1099      * @param password True if the node is a password.
   1100      *
   1101      * @throws IllegalStateException If called from an AccessibilityService.
   1102      */
   1103     public void setPassword(boolean password) {
   1104         setBooleanProperty(PROPERTY_PASSWORD, password);
   1105     }
   1106 
   1107     /**
   1108      * Gets if the node is scrollable.
   1109      *
   1110      * @return True if the node is scrollable, false otherwise.
   1111      */
   1112     public boolean isScrollable() {
   1113         return getBooleanProperty(PROPERTY_SCROLLABLE);
   1114     }
   1115 
   1116     /**
   1117      * Sets if the node is scrollable.
   1118      * <p>
   1119      *   <strong>Note:</strong> Cannot be called from an
   1120      *   {@link android.accessibilityservice.AccessibilityService}.
   1121      *   This class is made immutable before being delivered to an AccessibilityService.
   1122      * </p>
   1123      *
   1124      * @param scrollable True if the node is scrollable, false otherwise.
   1125      *
   1126      * @throws IllegalStateException If called from an AccessibilityService.
   1127      */
   1128     public void setScrollable(boolean scrollable) {
   1129         enforceNotSealed();
   1130         setBooleanProperty(PROPERTY_SCROLLABLE, scrollable);
   1131     }
   1132 
   1133     /**
   1134      * Gets the package this node comes from.
   1135      *
   1136      * @return The package name.
   1137      */
   1138     public CharSequence getPackageName() {
   1139         return mPackageName;
   1140     }
   1141 
   1142     /**
   1143      * Sets the package this node comes from.
   1144      * <p>
   1145      *   <strong>Note:</strong> Cannot be called from an
   1146      *   {@link android.accessibilityservice.AccessibilityService}.
   1147      *   This class is made immutable before being delivered to an AccessibilityService.
   1148      * </p>
   1149      *
   1150      * @param packageName The package name.
   1151      *
   1152      * @throws IllegalStateException If called from an AccessibilityService.
   1153      */
   1154     public void setPackageName(CharSequence packageName) {
   1155         enforceNotSealed();
   1156         mPackageName = packageName;
   1157     }
   1158 
   1159     /**
   1160      * Gets the class this node comes from.
   1161      *
   1162      * @return The class name.
   1163      */
   1164     public CharSequence getClassName() {
   1165         return mClassName;
   1166     }
   1167 
   1168     /**
   1169      * Sets the class this node comes from.
   1170      * <p>
   1171      *   <strong>Note:</strong> Cannot be called from an
   1172      *   {@link android.accessibilityservice.AccessibilityService}.
   1173      *   This class is made immutable before being delivered to an AccessibilityService.
   1174      * </p>
   1175      *
   1176      * @param className The class name.
   1177      *
   1178      * @throws IllegalStateException If called from an AccessibilityService.
   1179      */
   1180     public void setClassName(CharSequence className) {
   1181         enforceNotSealed();
   1182         mClassName = className;
   1183     }
   1184 
   1185     /**
   1186      * Gets the text of this node.
   1187      *
   1188      * @return The text.
   1189      */
   1190     public CharSequence getText() {
   1191         return mText;
   1192     }
   1193 
   1194     /**
   1195      * Sets the text of this node.
   1196      * <p>
   1197      *   <strong>Note:</strong> Cannot be called from an
   1198      *   {@link android.accessibilityservice.AccessibilityService}.
   1199      *   This class is made immutable before being delivered to an AccessibilityService.
   1200      * </p>
   1201      *
   1202      * @param text The text.
   1203      *
   1204      * @throws IllegalStateException If called from an AccessibilityService.
   1205      */
   1206     public void setText(CharSequence text) {
   1207         enforceNotSealed();
   1208         mText = text;
   1209     }
   1210 
   1211     /**
   1212      * Gets the content description of this node.
   1213      *
   1214      * @return The content description.
   1215      */
   1216     public CharSequence getContentDescription() {
   1217         return mContentDescription;
   1218     }
   1219 
   1220     /**
   1221      * Sets the content description of this node.
   1222      * <p>
   1223      *   <strong>Note:</strong> Cannot be called from an
   1224      *   {@link android.accessibilityservice.AccessibilityService}.
   1225      *   This class is made immutable before being delivered to an AccessibilityService.
   1226      * </p>
   1227      *
   1228      * @param contentDescription The content description.
   1229      *
   1230      * @throws IllegalStateException If called from an AccessibilityService.
   1231      */
   1232     public void setContentDescription(CharSequence contentDescription) {
   1233         enforceNotSealed();
   1234         mContentDescription = contentDescription;
   1235     }
   1236 
   1237     /**
   1238      * Sets the view for which the view represented by this info serves as a
   1239      * label for accessibility purposes.
   1240      *
   1241      * @param labeled The view for which this info serves as a label.
   1242      */
   1243     public void setLabelFor(View labeled) {
   1244         setLabelFor(labeled, UNDEFINED);
   1245     }
   1246 
   1247     /**
   1248      * Sets the view for which the view represented by this info serves as a
   1249      * label for accessibility purposes. If <code>virtualDescendantId</code>
   1250      * is {@link View#NO_ID} the root is set as the labeled.
   1251      * <p>
   1252      * A virtual descendant is an imaginary View that is reported as a part of the view
   1253      * hierarchy for accessibility purposes. This enables custom views that draw complex
   1254      * content to report themselves as a tree of virtual views, thus conveying their
   1255      * logical structure.
   1256      * </p>
   1257      * <p>
   1258      *   <strong>Note:</strong> Cannot be called from an
   1259      *   {@link android.accessibilityservice.AccessibilityService}.
   1260      *   This class is made immutable before being delivered to an AccessibilityService.
   1261      * </p>
   1262      *
   1263      * @param root The root whose virtual descendant serves as a label.
   1264      * @param virtualDescendantId The id of the virtual descendant.
   1265      */
   1266     public void setLabelFor(View root, int virtualDescendantId) {
   1267         enforceNotSealed();
   1268         final int rootAccessibilityViewId = (root != null)
   1269                 ? root.getAccessibilityViewId() : UNDEFINED;
   1270         mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   1271     }
   1272 
   1273     /**
   1274      * Gets the node info for which the view represented by this info serves as
   1275      * a label for accessibility purposes.
   1276      * <p>
   1277      *   <strong>Note:</strong> It is a client responsibility to recycle the
   1278      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   1279      *     to avoid creating of multiple instances.
   1280      * </p>
   1281      *
   1282      * @return The labeled info.
   1283      */
   1284     public AccessibilityNodeInfo getLabelFor() {
   1285         enforceSealed();
   1286         if (!canPerformRequestOverConnection(mLabelForId)) {
   1287             return null;
   1288         }
   1289         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1290         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
   1291                 mWindowId, mLabelForId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
   1292     }
   1293 
   1294     /**
   1295      * Sets the view which serves as the label of the view represented by
   1296      * this info for accessibility purposes.
   1297      *
   1298      * @param label The view that labels this node's source.
   1299      */
   1300     public void setLabeledBy(View label) {
   1301         setLabeledBy(label, UNDEFINED);
   1302     }
   1303 
   1304     /**
   1305      * Sets the view which serves as the label of the view represented by
   1306      * this info for accessibility purposes. If <code>virtualDescendantId</code>
   1307      * is {@link View#NO_ID} the root is set as the label.
   1308      * <p>
   1309      * A virtual descendant is an imaginary View that is reported as a part of the view
   1310      * hierarchy for accessibility purposes. This enables custom views that draw complex
   1311      * content to report themselves as a tree of virtual views, thus conveying their
   1312      * logical structure.
   1313      * </p>
   1314      * <p>
   1315      *   <strong>Note:</strong> Cannot be called from an
   1316      *   {@link android.accessibilityservice.AccessibilityService}.
   1317      *   This class is made immutable before being delivered to an AccessibilityService.
   1318      * </p>
   1319      *
   1320      * @param root The root whose virtual descendant labels this node's source.
   1321      * @param virtualDescendantId The id of the virtual descendant.
   1322      */
   1323     public void setLabeledBy(View root, int virtualDescendantId) {
   1324         enforceNotSealed();
   1325         final int rootAccessibilityViewId = (root != null)
   1326                 ? root.getAccessibilityViewId() : UNDEFINED;
   1327         mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   1328     }
   1329 
   1330     /**
   1331      * Gets the node info which serves as the label of the view represented by
   1332      * this info for accessibility purposes.
   1333      * <p>
   1334      *   <strong>Note:</strong> It is a client responsibility to recycle the
   1335      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   1336      *     to avoid creating of multiple instances.
   1337      * </p>
   1338      *
   1339      * @return The label.
   1340      */
   1341     public AccessibilityNodeInfo getLabeledBy() {
   1342         enforceSealed();
   1343         if (!canPerformRequestOverConnection(mLabeledById)) {
   1344             return null;
   1345         }
   1346         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1347         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
   1348                 mWindowId, mLabeledById, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
   1349     }
   1350 
   1351     /**
   1352      * Gets the value of a boolean property.
   1353      *
   1354      * @param property The property.
   1355      * @return The value.
   1356      */
   1357     private boolean getBooleanProperty(int property) {
   1358         return (mBooleanProperties & property) != 0;
   1359     }
   1360 
   1361     /**
   1362      * Sets a boolean property.
   1363      *
   1364      * @param property The property.
   1365      * @param value The value.
   1366      *
   1367      * @throws IllegalStateException If called from an AccessibilityService.
   1368      */
   1369     private void setBooleanProperty(int property, boolean value) {
   1370         enforceNotSealed();
   1371         if (value) {
   1372             mBooleanProperties |= property;
   1373         } else {
   1374             mBooleanProperties &= ~property;
   1375         }
   1376     }
   1377 
   1378     /**
   1379      * Sets the unique id of the IAccessibilityServiceConnection over which
   1380      * this instance can send requests to the system.
   1381      *
   1382      * @param connectionId The connection id.
   1383      *
   1384      * @hide
   1385      */
   1386     public void setConnectionId(int connectionId) {
   1387         enforceNotSealed();
   1388         mConnectionId = connectionId;
   1389     }
   1390 
   1391     /**
   1392      * {@inheritDoc}
   1393      */
   1394     public int describeContents() {
   1395         return 0;
   1396     }
   1397 
   1398     /**
   1399      * Gets the id of the source node.
   1400      *
   1401      * @return The id.
   1402      *
   1403      * @hide
   1404      */
   1405     public long getSourceNodeId() {
   1406         return mSourceNodeId;
   1407     }
   1408 
   1409     /**
   1410      * Sets if this instance is sealed.
   1411      *
   1412      * @param sealed Whether is sealed.
   1413      *
   1414      * @hide
   1415      */
   1416     public void setSealed(boolean sealed) {
   1417         mSealed = sealed;
   1418     }
   1419 
   1420     /**
   1421      * Gets if this instance is sealed.
   1422      *
   1423      * @return Whether is sealed.
   1424      *
   1425      * @hide
   1426      */
   1427     public boolean isSealed() {
   1428         return mSealed;
   1429     }
   1430 
   1431     /**
   1432      * Enforces that this instance is sealed.
   1433      *
   1434      * @throws IllegalStateException If this instance is not sealed.
   1435      *
   1436      * @hide
   1437      */
   1438     protected void enforceSealed() {
   1439         if (!isSealed()) {
   1440             throw new IllegalStateException("Cannot perform this "
   1441                     + "action on a not sealed instance.");
   1442         }
   1443     }
   1444 
   1445     private void enforceValidFocusDirection(int direction) {
   1446         switch (direction) {
   1447             case View.FOCUS_DOWN:
   1448             case View.FOCUS_UP:
   1449             case View.FOCUS_LEFT:
   1450             case View.FOCUS_RIGHT:
   1451             case View.FOCUS_FORWARD:
   1452             case View.FOCUS_BACKWARD:
   1453                 return;
   1454             default:
   1455                 throw new IllegalArgumentException("Unknown direction: " + direction);
   1456         }
   1457     }
   1458 
   1459     private void enforceValidFocusType(int focusType) {
   1460         switch (focusType) {
   1461             case FOCUS_INPUT:
   1462             case FOCUS_ACCESSIBILITY:
   1463                 return;
   1464             default:
   1465                 throw new IllegalArgumentException("Unknown focus type: " + focusType);
   1466         }
   1467     }
   1468 
   1469     /**
   1470      * Enforces that this instance is not sealed.
   1471      *
   1472      * @throws IllegalStateException If this instance is sealed.
   1473      *
   1474      * @hide
   1475      */
   1476     protected void enforceNotSealed() {
   1477         if (isSealed()) {
   1478             throw new IllegalStateException("Cannot perform this "
   1479                     + "action on a sealed instance.");
   1480         }
   1481     }
   1482 
   1483     /**
   1484      * Returns a cached instance if such is available otherwise a new one
   1485      * and sets the source.
   1486      *
   1487      * @param source The source view.
   1488      * @return An instance.
   1489      *
   1490      * @see #setSource(View)
   1491      */
   1492     public static AccessibilityNodeInfo obtain(View source) {
   1493         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   1494         info.setSource(source);
   1495         return info;
   1496     }
   1497 
   1498     /**
   1499      * Returns a cached instance if such is available otherwise a new one
   1500      * and sets the source.
   1501      *
   1502      * @param root The root of the virtual subtree.
   1503      * @param virtualDescendantId The id of the virtual descendant.
   1504      * @return An instance.
   1505      *
   1506      * @see #setSource(View, int)
   1507      */
   1508     public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
   1509         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   1510         info.setSource(root, virtualDescendantId);
   1511         return info;
   1512     }
   1513 
   1514     /**
   1515      * Returns a cached instance if such is available otherwise a new one.
   1516      *
   1517      * @return An instance.
   1518      */
   1519     public static AccessibilityNodeInfo obtain() {
   1520         synchronized (sPoolLock) {
   1521             if (sPool != null) {
   1522                 AccessibilityNodeInfo info = sPool;
   1523                 sPool = sPool.mNext;
   1524                 sPoolSize--;
   1525                 info.mNext = null;
   1526                 info.mIsInPool = false;
   1527                 return info;
   1528             }
   1529             return new AccessibilityNodeInfo();
   1530         }
   1531     }
   1532 
   1533     /**
   1534      * Returns a cached instance if such is available or a new one is
   1535      * create. The returned instance is initialized from the given
   1536      * <code>info</code>.
   1537      *
   1538      * @param info The other info.
   1539      * @return An instance.
   1540      */
   1541     public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
   1542         AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
   1543         infoClone.init(info);
   1544         return infoClone;
   1545     }
   1546 
   1547     /**
   1548      * Return an instance back to be reused.
   1549      * <p>
   1550      * <strong>Note:</strong> You must not touch the object after calling this function.
   1551      *
   1552      * @throws IllegalStateException If the info is already recycled.
   1553      */
   1554     public void recycle() {
   1555         if (mIsInPool) {
   1556             throw new IllegalStateException("Info already recycled!");
   1557         }
   1558         clear();
   1559         synchronized (sPoolLock) {
   1560             if (sPoolSize <= MAX_POOL_SIZE) {
   1561                 mNext = sPool;
   1562                 sPool = this;
   1563                 mIsInPool = true;
   1564                 sPoolSize++;
   1565             }
   1566         }
   1567     }
   1568 
   1569     /**
   1570      * {@inheritDoc}
   1571      * <p>
   1572      *   <strong>Note:</strong> After the instance is written to a parcel it
   1573      *      is recycled. You must not touch the object after calling this function.
   1574      * </p>
   1575      */
   1576     public void writeToParcel(Parcel parcel, int flags) {
   1577         parcel.writeInt(isSealed() ? 1 : 0);
   1578         parcel.writeLong(mSourceNodeId);
   1579         parcel.writeInt(mWindowId);
   1580         parcel.writeLong(mParentNodeId);
   1581         parcel.writeLong(mLabelForId);
   1582         parcel.writeLong(mLabeledById);
   1583         parcel.writeInt(mConnectionId);
   1584 
   1585         SparseLongArray childIds = mChildNodeIds;
   1586         final int childIdsSize = childIds.size();
   1587         parcel.writeInt(childIdsSize);
   1588         for (int i = 0; i < childIdsSize; i++) {
   1589             parcel.writeLong(childIds.valueAt(i));
   1590         }
   1591 
   1592         parcel.writeInt(mBoundsInParent.top);
   1593         parcel.writeInt(mBoundsInParent.bottom);
   1594         parcel.writeInt(mBoundsInParent.left);
   1595         parcel.writeInt(mBoundsInParent.right);
   1596 
   1597         parcel.writeInt(mBoundsInScreen.top);
   1598         parcel.writeInt(mBoundsInScreen.bottom);
   1599         parcel.writeInt(mBoundsInScreen.left);
   1600         parcel.writeInt(mBoundsInScreen.right);
   1601 
   1602         parcel.writeInt(mActions);
   1603 
   1604         parcel.writeInt(mMovementGranularities);
   1605 
   1606         parcel.writeInt(mBooleanProperties);
   1607 
   1608         parcel.writeCharSequence(mPackageName);
   1609         parcel.writeCharSequence(mClassName);
   1610         parcel.writeCharSequence(mText);
   1611         parcel.writeCharSequence(mContentDescription);
   1612 
   1613         // Since instances of this class are fetched via synchronous i.e. blocking
   1614         // calls in IPCs we always recycle as soon as the instance is marshaled.
   1615         recycle();
   1616     }
   1617 
   1618     /**
   1619      * Initializes this instance from another one.
   1620      *
   1621      * @param other The other instance.
   1622      */
   1623     @SuppressWarnings("unchecked")
   1624     private void init(AccessibilityNodeInfo other) {
   1625         mSealed = other.mSealed;
   1626         mSourceNodeId = other.mSourceNodeId;
   1627         mParentNodeId = other.mParentNodeId;
   1628         mLabelForId = other.mLabelForId;
   1629         mLabeledById = other.mLabeledById;
   1630         mWindowId = other.mWindowId;
   1631         mConnectionId = other.mConnectionId;
   1632         mBoundsInParent.set(other.mBoundsInParent);
   1633         mBoundsInScreen.set(other.mBoundsInScreen);
   1634         mPackageName = other.mPackageName;
   1635         mClassName = other.mClassName;
   1636         mText = other.mText;
   1637         mContentDescription = other.mContentDescription;
   1638         mActions= other.mActions;
   1639         mBooleanProperties = other.mBooleanProperties;
   1640         mMovementGranularities = other.mMovementGranularities;
   1641         final int otherChildIdCount = other.mChildNodeIds.size();
   1642         for (int i = 0; i < otherChildIdCount; i++) {
   1643             mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));
   1644         }
   1645     }
   1646 
   1647     /**
   1648      * Creates a new instance from a {@link Parcel}.
   1649      *
   1650      * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
   1651      */
   1652     private void initFromParcel(Parcel parcel) {
   1653         mSealed = (parcel.readInt()  == 1);
   1654         mSourceNodeId = parcel.readLong();
   1655         mWindowId = parcel.readInt();
   1656         mParentNodeId = parcel.readLong();
   1657         mLabelForId = parcel.readLong();
   1658         mLabeledById = parcel.readLong();
   1659         mConnectionId = parcel.readInt();
   1660 
   1661         SparseLongArray childIds = mChildNodeIds;
   1662         final int childrenSize = parcel.readInt();
   1663         for (int i = 0; i < childrenSize; i++) {
   1664             final long childId = parcel.readLong();
   1665             childIds.put(i, childId);
   1666         }
   1667 
   1668         mBoundsInParent.top = parcel.readInt();
   1669         mBoundsInParent.bottom = parcel.readInt();
   1670         mBoundsInParent.left = parcel.readInt();
   1671         mBoundsInParent.right = parcel.readInt();
   1672 
   1673         mBoundsInScreen.top = parcel.readInt();
   1674         mBoundsInScreen.bottom = parcel.readInt();
   1675         mBoundsInScreen.left = parcel.readInt();
   1676         mBoundsInScreen.right = parcel.readInt();
   1677 
   1678         mActions = parcel.readInt();
   1679 
   1680         mMovementGranularities = parcel.readInt();
   1681 
   1682         mBooleanProperties = parcel.readInt();
   1683 
   1684         mPackageName = parcel.readCharSequence();
   1685         mClassName = parcel.readCharSequence();
   1686         mText = parcel.readCharSequence();
   1687         mContentDescription = parcel.readCharSequence();
   1688     }
   1689 
   1690     /**
   1691      * Clears the state of this instance.
   1692      */
   1693     private void clear() {
   1694         mSealed = false;
   1695         mSourceNodeId = ROOT_NODE_ID;
   1696         mParentNodeId = ROOT_NODE_ID;
   1697         mLabelForId = ROOT_NODE_ID;
   1698         mLabeledById = ROOT_NODE_ID;
   1699         mWindowId = UNDEFINED;
   1700         mConnectionId = UNDEFINED;
   1701         mMovementGranularities = 0;
   1702         mChildNodeIds.clear();
   1703         mBoundsInParent.set(0, 0, 0, 0);
   1704         mBoundsInScreen.set(0, 0, 0, 0);
   1705         mBooleanProperties = 0;
   1706         mPackageName = null;
   1707         mClassName = null;
   1708         mText = null;
   1709         mContentDescription = null;
   1710         mActions = 0;
   1711     }
   1712 
   1713     /**
   1714      * Gets the human readable action symbolic name.
   1715      *
   1716      * @param action The action.
   1717      * @return The symbolic name.
   1718      */
   1719     private static String getActionSymbolicName(int action) {
   1720         switch (action) {
   1721             case ACTION_FOCUS:
   1722                 return "ACTION_FOCUS";
   1723             case ACTION_CLEAR_FOCUS:
   1724                 return "ACTION_CLEAR_FOCUS";
   1725             case ACTION_SELECT:
   1726                 return "ACTION_SELECT";
   1727             case ACTION_CLEAR_SELECTION:
   1728                 return "ACTION_CLEAR_SELECTION";
   1729             case ACTION_CLICK:
   1730                 return "ACTION_CLICK";
   1731             case ACTION_LONG_CLICK:
   1732                 return "ACTION_LONG_CLICK";
   1733             case ACTION_ACCESSIBILITY_FOCUS:
   1734                 return "ACTION_ACCESSIBILITY_FOCUS";
   1735             case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
   1736                 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
   1737             case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
   1738                 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
   1739             case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
   1740                 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
   1741             case ACTION_NEXT_HTML_ELEMENT:
   1742                 return "ACTION_NEXT_HTML_ELEMENT";
   1743             case ACTION_PREVIOUS_HTML_ELEMENT:
   1744                 return "ACTION_PREVIOUS_HTML_ELEMENT";
   1745             case ACTION_SCROLL_FORWARD:
   1746                 return "ACTION_SCROLL_FORWARD";
   1747             case ACTION_SCROLL_BACKWARD:
   1748                 return "ACTION_SCROLL_BACKWARD";
   1749             default:
   1750                 throw new IllegalArgumentException("Unknown action: " + action);
   1751         }
   1752     }
   1753 
   1754     /**
   1755      * Gets the human readable movement granularity symbolic name.
   1756      *
   1757      * @param granularity The granularity.
   1758      * @return The symbolic name.
   1759      */
   1760     private static String getMovementGranularitySymbolicName(int granularity) {
   1761         switch (granularity) {
   1762             case MOVEMENT_GRANULARITY_CHARACTER:
   1763                 return "MOVEMENT_GRANULARITY_CHARACTER";
   1764             case MOVEMENT_GRANULARITY_WORD:
   1765                 return "MOVEMENT_GRANULARITY_WORD";
   1766             case MOVEMENT_GRANULARITY_LINE:
   1767                 return "MOVEMENT_GRANULARITY_LINE";
   1768             case MOVEMENT_GRANULARITY_PARAGRAPH:
   1769                 return "MOVEMENT_GRANULARITY_PARAGRAPH";
   1770             case MOVEMENT_GRANULARITY_PAGE:
   1771                 return "MOVEMENT_GRANULARITY_PAGE";
   1772             default:
   1773                 throw new IllegalArgumentException("Unknown movement granularity: " + granularity);
   1774         }
   1775     }
   1776 
   1777     private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
   1778         return (mWindowId != UNDEFINED
   1779                 && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
   1780                 && mConnectionId != UNDEFINED);
   1781     }
   1782 
   1783     @Override
   1784     public boolean equals(Object object) {
   1785         if (this == object) {
   1786             return true;
   1787         }
   1788         if (object == null) {
   1789             return false;
   1790         }
   1791         if (getClass() != object.getClass()) {
   1792             return false;
   1793         }
   1794         AccessibilityNodeInfo other = (AccessibilityNodeInfo) object;
   1795         if (mSourceNodeId != other.mSourceNodeId) {
   1796             return false;
   1797         }
   1798         if (mWindowId != other.mWindowId) {
   1799             return false;
   1800         }
   1801         return true;
   1802     }
   1803 
   1804     @Override
   1805     public int hashCode() {
   1806         final int prime = 31;
   1807         int result = 1;
   1808         result = prime * result + getAccessibilityViewId(mSourceNodeId);
   1809         result = prime * result + getVirtualDescendantId(mSourceNodeId);
   1810         result = prime * result + mWindowId;
   1811         return result;
   1812     }
   1813 
   1814     @Override
   1815     public String toString() {
   1816         StringBuilder builder = new StringBuilder();
   1817         builder.append(super.toString());
   1818 
   1819         if (DEBUG) {
   1820             builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
   1821             builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
   1822             builder.append("; mParentNodeId: " + mParentNodeId);
   1823 
   1824             int granularities = mMovementGranularities;
   1825             builder.append("; MovementGranularities: [");
   1826             while (granularities != 0) {
   1827                 final int granularity = 1 << Integer.numberOfTrailingZeros(granularities);
   1828                 granularities &= ~granularity;
   1829                 builder.append(getMovementGranularitySymbolicName(granularity));
   1830                 if (granularities != 0) {
   1831                     builder.append(", ");
   1832                 }
   1833             }
   1834             builder.append("]");
   1835 
   1836             SparseLongArray childIds = mChildNodeIds;
   1837             builder.append("; childAccessibilityIds: [");
   1838             for (int i = 0, count = childIds.size(); i < count; i++) {
   1839                 builder.append(childIds.valueAt(i));
   1840                 if (i < count - 1) {
   1841                     builder.append(", ");
   1842                 }
   1843             }
   1844             builder.append("]");
   1845         }
   1846 
   1847         builder.append("; boundsInParent: " + mBoundsInParent);
   1848         builder.append("; boundsInScreen: " + mBoundsInScreen);
   1849 
   1850         builder.append("; packageName: ").append(mPackageName);
   1851         builder.append("; className: ").append(mClassName);
   1852         builder.append("; text: ").append(mText);
   1853         builder.append("; contentDescription: ").append(mContentDescription);
   1854 
   1855         builder.append("; checkable: ").append(isCheckable());
   1856         builder.append("; checked: ").append(isChecked());
   1857         builder.append("; focusable: ").append(isFocusable());
   1858         builder.append("; focused: ").append(isFocused());
   1859         builder.append("; selected: ").append(isSelected());
   1860         builder.append("; clickable: ").append(isClickable());
   1861         builder.append("; longClickable: ").append(isLongClickable());
   1862         builder.append("; enabled: ").append(isEnabled());
   1863         builder.append("; password: ").append(isPassword());
   1864         builder.append("; scrollable: " + isScrollable());
   1865 
   1866         builder.append("; [");
   1867         for (int actionBits = mActions; actionBits != 0;) {
   1868             final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
   1869             actionBits &= ~action;
   1870             builder.append(getActionSymbolicName(action));
   1871             if (actionBits != 0) {
   1872                 builder.append(", ");
   1873             }
   1874         }
   1875         builder.append("]");
   1876 
   1877         return builder.toString();
   1878     }
   1879 
   1880     /**
   1881      * @see Parcelable.Creator
   1882      */
   1883     public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
   1884             new Parcelable.Creator<AccessibilityNodeInfo>() {
   1885         public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
   1886             AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   1887             info.initFromParcel(parcel);
   1888             return info;
   1889         }
   1890 
   1891         public AccessibilityNodeInfo[] newArray(int size) {
   1892             return new AccessibilityNodeInfo[size];
   1893         }
   1894     };
   1895 }
   1896