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 
    369     private int mBooleanProperties;
    370     private final Rect mBoundsInParent = new Rect();
    371     private final Rect mBoundsInScreen = new Rect();
    372 
    373     private CharSequence mPackageName;
    374     private CharSequence mClassName;
    375     private CharSequence mText;
    376     private CharSequence mContentDescription;
    377 
    378     private final SparseLongArray mChildNodeIds = new SparseLongArray();
    379     private int mActions;
    380 
    381     private int mMovementGranularities;
    382 
    383     private int mConnectionId = UNDEFINED;
    384 
    385     // TODO: These are a workaround for 6623031. Remove when fixed.
    386     private int mActualAndReportedWindowLeftDelta;
    387     private int mActualAndReportedWindowTopDelta;
    388 
    389     /**
    390      * Hide constructor from clients.
    391      */
    392     private AccessibilityNodeInfo() {
    393         /* do nothing */
    394     }
    395 
    396     /**
    397      * Sets the source.
    398      * <p>
    399      *   <strong>Note:</strong> Cannot be called from an
    400      *   {@link android.accessibilityservice.AccessibilityService}.
    401      *   This class is made immutable before being delivered to an AccessibilityService.
    402      * </p>
    403      *
    404      * @param source The info source.
    405      */
    406     public void setSource(View source) {
    407         setSource(source, UNDEFINED);
    408     }
    409 
    410     /**
    411      * Sets the source to be a virtual descendant of the given <code>root</code>.
    412      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
    413      * is set as the source.
    414      * <p>
    415      * A virtual descendant is an imaginary View that is reported as a part of the view
    416      * hierarchy for accessibility purposes. This enables custom views that draw complex
    417      * content to report themselves as a tree of virtual views, thus conveying their
    418      * logical structure.
    419      * </p>
    420      * <p>
    421      *   <strong>Note:</strong> Cannot be called from an
    422      *   {@link android.accessibilityservice.AccessibilityService}.
    423      *   This class is made immutable before being delivered to an AccessibilityService.
    424      * </p>
    425      *
    426      * @param root The root of the virtual subtree.
    427      * @param virtualDescendantId The id of the virtual descendant.
    428      */
    429     public void setSource(View root, int virtualDescendantId) {
    430         enforceNotSealed();
    431         mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
    432         final int rootAccessibilityViewId =
    433             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
    434         mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
    435         if (root != null) {
    436             mActualAndReportedWindowLeftDelta = root.getActualAndReportedWindowLeftDelta();
    437             mActualAndReportedWindowTopDelta = root.getActualAndReportedWindowTopDelta();
    438         }
    439     }
    440 
    441     /**
    442      * Find the view that has the specified focus type. The search starts from
    443      * the view represented by this node info.
    444      *
    445      * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
    446      *         {@link #FOCUS_ACCESSIBILITY}.
    447      * @return The node info of the focused view or null.
    448      *
    449      * @see #FOCUS_INPUT
    450      * @see #FOCUS_ACCESSIBILITY
    451      */
    452     public AccessibilityNodeInfo findFocus(int focus) {
    453         enforceSealed();
    454         enforceValidFocusType(focus);
    455         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    456             return null;
    457         }
    458         return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId,
    459                 mSourceNodeId, focus);
    460     }
    461 
    462     /**
    463      * Searches for the nearest view in the specified direction that can take
    464      * the input focus.
    465      *
    466      * @param direction The direction. Can be one of:
    467      *     {@link View#FOCUS_DOWN},
    468      *     {@link View#FOCUS_UP},
    469      *     {@link View#FOCUS_LEFT},
    470      *     {@link View#FOCUS_RIGHT},
    471      *     {@link View#FOCUS_FORWARD},
    472      *     {@link View#FOCUS_BACKWARD}.
    473      *
    474      * @return The node info for the view that can take accessibility focus.
    475      */
    476     public AccessibilityNodeInfo focusSearch(int direction) {
    477         enforceSealed();
    478         enforceValidFocusDirection(direction);
    479         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    480             return null;
    481         }
    482         return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId,
    483                 mSourceNodeId, direction);
    484     }
    485 
    486     /**
    487      * Gets the id of the window from which the info comes from.
    488      *
    489      * @return The window id.
    490      */
    491     public int getWindowId() {
    492         return mWindowId;
    493     }
    494 
    495     /**
    496      * @return The ids of the children.
    497      *
    498      * @hide
    499      */
    500     public SparseLongArray getChildNodeIds() {
    501         return mChildNodeIds;
    502     }
    503 
    504     /**
    505      * Gets the number of children.
    506      *
    507      * @return The child count.
    508      */
    509     public int getChildCount() {
    510         return mChildNodeIds.size();
    511     }
    512 
    513     /**
    514      * Get the child at given index.
    515      * <p>
    516      *   <strong>Note:</strong> It is a client responsibility to recycle the
    517      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
    518      *     to avoid creating of multiple instances.
    519      * </p>
    520      *
    521      * @param index The child index.
    522      * @return The child node.
    523      *
    524      * @throws IllegalStateException If called outside of an AccessibilityService.
    525      *
    526      */
    527     public AccessibilityNodeInfo getChild(int index) {
    528         enforceSealed();
    529         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    530             return null;
    531         }
    532         final long childId = mChildNodeIds.get(index);
    533         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    534         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
    535                 childId, FLAG_PREFETCH_DESCENDANTS);
    536     }
    537 
    538     /**
    539      * Adds a child.
    540      * <p>
    541      * <strong>Note:</strong> Cannot be called from an
    542      * {@link android.accessibilityservice.AccessibilityService}.
    543      * This class is made immutable before being delivered to an AccessibilityService.
    544      * </p>
    545      *
    546      * @param child The child.
    547      *
    548      * @throws IllegalStateException If called from an AccessibilityService.
    549      */
    550     public void addChild(View child) {
    551         addChild(child, UNDEFINED);
    552     }
    553 
    554     /**
    555      * Adds a virtual child which is a descendant of the given <code>root</code>.
    556      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
    557      * is added as a child.
    558      * <p>
    559      * A virtual descendant is an imaginary View that is reported as a part of the view
    560      * hierarchy for accessibility purposes. This enables custom views that draw complex
    561      * content to report them selves as a tree of virtual views, thus conveying their
    562      * logical structure.
    563      * </p>
    564      *
    565      * @param root The root of the virtual subtree.
    566      * @param virtualDescendantId The id of the virtual child.
    567      */
    568     public void addChild(View root, int virtualDescendantId) {
    569         enforceNotSealed();
    570         final int index = mChildNodeIds.size();
    571         final int rootAccessibilityViewId =
    572             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
    573         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
    574         mChildNodeIds.put(index, childNodeId);
    575     }
    576 
    577     /**
    578      * Gets the actions that can be performed on the node.
    579      *
    580      * @return The bit mask of with actions.
    581      *
    582      * @see AccessibilityNodeInfo#ACTION_FOCUS
    583      * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
    584      * @see AccessibilityNodeInfo#ACTION_SELECT
    585      * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
    586      * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS
    587      * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS
    588      * @see AccessibilityNodeInfo#ACTION_CLICK
    589      * @see AccessibilityNodeInfo#ACTION_LONG_CLICK
    590      * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
    591      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
    592      * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT
    593      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT
    594      * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD
    595      * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD
    596      */
    597     public int getActions() {
    598         return mActions;
    599     }
    600 
    601     /**
    602      * Adds an action that can be performed on the node.
    603      * <p>
    604      *   <strong>Note:</strong> Cannot be called from an
    605      *   {@link android.accessibilityservice.AccessibilityService}.
    606      *   This class is made immutable before being delivered to an AccessibilityService.
    607      * </p>
    608      *
    609      * @param action The action.
    610      *
    611      * @throws IllegalStateException If called from an AccessibilityService.
    612      */
    613     public void addAction(int action) {
    614         enforceNotSealed();
    615         mActions |= action;
    616     }
    617 
    618     /**
    619      * Sets the movement granularities for traversing the text of this node.
    620      * <p>
    621      *   <strong>Note:</strong> Cannot be called from an
    622      *   {@link android.accessibilityservice.AccessibilityService}.
    623      *   This class is made immutable before being delivered to an AccessibilityService.
    624      * </p>
    625      *
    626      * @param granularities The bit mask with granularities.
    627      *
    628      * @throws IllegalStateException If called from an AccessibilityService.
    629      */
    630     public void setMovementGranularities(int granularities) {
    631         enforceNotSealed();
    632         mMovementGranularities = granularities;
    633     }
    634 
    635     /**
    636      * Gets the movement granularities for traversing the text of this node.
    637      *
    638      * @return The bit mask with granularities.
    639      */
    640     public int getMovementGranularities() {
    641         return mMovementGranularities;
    642     }
    643 
    644     /**
    645      * Performs an action on the node.
    646      * <p>
    647      *   <strong>Note:</strong> An action can be performed only if the request is made
    648      *   from an {@link android.accessibilityservice.AccessibilityService}.
    649      * </p>
    650      *
    651      * @param action The action to perform.
    652      * @return True if the action was performed.
    653      *
    654      * @throws IllegalStateException If called outside of an AccessibilityService.
    655      */
    656     public boolean performAction(int action) {
    657         enforceSealed();
    658         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    659             return false;
    660         }
    661         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    662         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
    663                 action, null);
    664     }
    665 
    666     /**
    667      * Performs an action on the node.
    668      * <p>
    669      *   <strong>Note:</strong> An action can be performed only if the request is made
    670      *   from an {@link android.accessibilityservice.AccessibilityService}.
    671      * </p>
    672      *
    673      * @param action The action to perform.
    674      * @param arguments A bundle with additional arguments.
    675      * @return True if the action was performed.
    676      *
    677      * @throws IllegalStateException If called outside of an AccessibilityService.
    678      */
    679     public boolean performAction(int action, Bundle arguments) {
    680         enforceSealed();
    681         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    682             return false;
    683         }
    684         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    685         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
    686                 action, arguments);
    687     }
    688 
    689     /**
    690      * Finds {@link AccessibilityNodeInfo}s by text. The match is case
    691      * insensitive containment. The search is relative to this info i.e.
    692      * this info is the root of the traversed tree.
    693      *
    694      * <p>
    695      *   <strong>Note:</strong> It is a client responsibility to recycle the
    696      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
    697      *     to avoid creating of multiple instances.
    698      * </p>
    699      *
    700      * @param text The searched text.
    701      * @return A list of node info.
    702      */
    703     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
    704         enforceSealed();
    705         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    706             return Collections.emptyList();
    707         }
    708         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    709         return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
    710                 text);
    711     }
    712 
    713     /**
    714      * Gets the parent.
    715      * <p>
    716      *   <strong>Note:</strong> It is a client responsibility to recycle the
    717      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
    718      *     to avoid creating of multiple instances.
    719      * </p>
    720      *
    721      * @return The parent.
    722      */
    723     public AccessibilityNodeInfo getParent() {
    724         enforceSealed();
    725         if (!canPerformRequestOverConnection(mParentNodeId)) {
    726             return null;
    727         }
    728         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    729         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
    730                 mWindowId, mParentNodeId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
    731     }
    732 
    733     /**
    734      * @return The parent node id.
    735      *
    736      * @hide
    737      */
    738     public long getParentNodeId() {
    739         return mParentNodeId;
    740     }
    741 
    742     /**
    743      * Sets the parent.
    744      * <p>
    745      *   <strong>Note:</strong> Cannot be called from an
    746      *   {@link android.accessibilityservice.AccessibilityService}.
    747      *   This class is made immutable before being delivered to an AccessibilityService.
    748      * </p>
    749      *
    750      * @param parent The parent.
    751      *
    752      * @throws IllegalStateException If called from an AccessibilityService.
    753      */
    754     public void setParent(View parent) {
    755         setParent(parent, UNDEFINED);
    756     }
    757 
    758     /**
    759      * Sets the parent to be a virtual descendant of the given <code>root</code>.
    760      * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
    761      * is set as the parent.
    762      * <p>
    763      * A virtual descendant is an imaginary View that is reported as a part of the view
    764      * hierarchy for accessibility purposes. This enables custom views that draw complex
    765      * content to report them selves as a tree of virtual views, thus conveying their
    766      * logical structure.
    767      * </p>
    768      * <p>
    769      *   <strong>Note:</strong> Cannot be called from an
    770      *   {@link android.accessibilityservice.AccessibilityService}.
    771      *   This class is made immutable before being delivered to an AccessibilityService.
    772      * </p>
    773      *
    774      * @param root The root of the virtual subtree.
    775      * @param virtualDescendantId The id of the virtual descendant.
    776      */
    777     public void setParent(View root, int virtualDescendantId) {
    778         enforceNotSealed();
    779         final int rootAccessibilityViewId =
    780             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
    781         mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
    782     }
    783 
    784     /**
    785      * Gets the node bounds in parent coordinates.
    786      *
    787      * @param outBounds The output node bounds.
    788      */
    789     public void getBoundsInParent(Rect outBounds) {
    790         outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
    791                 mBoundsInParent.right, mBoundsInParent.bottom);
    792     }
    793 
    794     /**
    795      * Sets the node bounds in parent coordinates.
    796      * <p>
    797      *   <strong>Note:</strong> Cannot be called from an
    798      *   {@link android.accessibilityservice.AccessibilityService}.
    799      *   This class is made immutable before being delivered to an AccessibilityService.
    800      * </p>
    801      *
    802      * @param bounds The node bounds.
    803      *
    804      * @throws IllegalStateException If called from an AccessibilityService.
    805      */
    806     public void setBoundsInParent(Rect bounds) {
    807         enforceNotSealed();
    808         mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
    809     }
    810 
    811     /**
    812      * Gets the node bounds in screen coordinates.
    813      *
    814      * @param outBounds The output node bounds.
    815      */
    816     public void getBoundsInScreen(Rect outBounds) {
    817         outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top,
    818                 mBoundsInScreen.right, mBoundsInScreen.bottom);
    819     }
    820 
    821     /**
    822      * Sets the node bounds in screen coordinates.
    823      * <p>
    824      *   <strong>Note:</strong> Cannot be called from an
    825      *   {@link android.accessibilityservice.AccessibilityService}.
    826      *   This class is made immutable before being delivered to an AccessibilityService.
    827      * </p>
    828      *
    829      * @param bounds The node bounds.
    830      *
    831      * @throws IllegalStateException If called from an AccessibilityService.
    832      */
    833     public void setBoundsInScreen(Rect bounds) {
    834         enforceNotSealed();
    835         mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
    836         mBoundsInScreen.offset(mActualAndReportedWindowLeftDelta, mActualAndReportedWindowTopDelta);
    837     }
    838 
    839     /**
    840      * Gets whether this node is checkable.
    841      *
    842      * @return True if the node is checkable.
    843      */
    844     public boolean isCheckable() {
    845         return getBooleanProperty(PROPERTY_CHECKABLE);
    846     }
    847 
    848     /**
    849      * Sets whether this node is checkable.
    850      * <p>
    851      *   <strong>Note:</strong> Cannot be called from an
    852      *   {@link android.accessibilityservice.AccessibilityService}.
    853      *   This class is made immutable before being delivered to an AccessibilityService.
    854      * </p>
    855      *
    856      * @param checkable True if the node is checkable.
    857      *
    858      * @throws IllegalStateException If called from an AccessibilityService.
    859      */
    860     public void setCheckable(boolean checkable) {
    861         setBooleanProperty(PROPERTY_CHECKABLE, checkable);
    862     }
    863 
    864     /**
    865      * Gets whether this node is checked.
    866      *
    867      * @return True if the node is checked.
    868      */
    869     public boolean isChecked() {
    870         return getBooleanProperty(PROPERTY_CHECKED);
    871     }
    872 
    873     /**
    874      * Sets whether this node is checked.
    875      * <p>
    876      *   <strong>Note:</strong> Cannot be called from an
    877      *   {@link android.accessibilityservice.AccessibilityService}.
    878      *   This class is made immutable before being delivered to an AccessibilityService.
    879      * </p>
    880      *
    881      * @param checked True if the node is checked.
    882      *
    883      * @throws IllegalStateException If called from an AccessibilityService.
    884      */
    885     public void setChecked(boolean checked) {
    886         setBooleanProperty(PROPERTY_CHECKED, checked);
    887     }
    888 
    889     /**
    890      * Gets whether this node is focusable.
    891      *
    892      * @return True if the node is focusable.
    893      */
    894     public boolean isFocusable() {
    895         return getBooleanProperty(PROPERTY_FOCUSABLE);
    896     }
    897 
    898     /**
    899      * Sets whether this node is focusable.
    900      * <p>
    901      *   <strong>Note:</strong> Cannot be called from an
    902      *   {@link android.accessibilityservice.AccessibilityService}.
    903      *   This class is made immutable before being delivered to an AccessibilityService.
    904      * </p>
    905      *
    906      * @param focusable True if the node is focusable.
    907      *
    908      * @throws IllegalStateException If called from an AccessibilityService.
    909      */
    910     public void setFocusable(boolean focusable) {
    911         setBooleanProperty(PROPERTY_FOCUSABLE, focusable);
    912     }
    913 
    914     /**
    915      * Gets whether this node is focused.
    916      *
    917      * @return True if the node is focused.
    918      */
    919     public boolean isFocused() {
    920         return getBooleanProperty(PROPERTY_FOCUSED);
    921     }
    922 
    923     /**
    924      * Sets whether this node is focused.
    925      * <p>
    926      *   <strong>Note:</strong> Cannot be called from an
    927      *   {@link android.accessibilityservice.AccessibilityService}.
    928      *   This class is made immutable before being delivered to an AccessibilityService.
    929      * </p>
    930      *
    931      * @param focused True if the node is focused.
    932      *
    933      * @throws IllegalStateException If called from an AccessibilityService.
    934      */
    935     public void setFocused(boolean focused) {
    936         setBooleanProperty(PROPERTY_FOCUSED, focused);
    937     }
    938 
    939     /**
    940      * Sets whether this node is visible to the user.
    941      *
    942      * @return Whether the node is visible to the user.
    943      */
    944     public boolean isVisibleToUser() {
    945         return getBooleanProperty(PROPERTY_VISIBLE_TO_USER);
    946     }
    947 
    948     /**
    949      * Sets whether this node is visible to the user.
    950      * <p>
    951      *   <strong>Note:</strong> Cannot be called from an
    952      *   {@link android.accessibilityservice.AccessibilityService}.
    953      *   This class is made immutable before being delivered to an AccessibilityService.
    954      * </p>
    955      *
    956      * @param visibleToUser Whether the node is visible to the user.
    957      *
    958      * @throws IllegalStateException If called from an AccessibilityService.
    959      */
    960     public void setVisibleToUser(boolean visibleToUser) {
    961         setBooleanProperty(PROPERTY_VISIBLE_TO_USER, visibleToUser);
    962     }
    963 
    964     /**
    965      * Gets whether this node is accessibility focused.
    966      *
    967      * @return True if the node is accessibility focused.
    968      */
    969     public boolean isAccessibilityFocused() {
    970         return getBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED);
    971     }
    972 
    973     /**
    974      * Sets whether this node is accessibility focused.
    975      * <p>
    976      *   <strong>Note:</strong> Cannot be called from an
    977      *   {@link android.accessibilityservice.AccessibilityService}.
    978      *   This class is made immutable before being delivered to an AccessibilityService.
    979      * </p>
    980      *
    981      * @param focused True if the node is accessibility focused.
    982      *
    983      * @throws IllegalStateException If called from an AccessibilityService.
    984      */
    985     public void setAccessibilityFocused(boolean focused) {
    986         setBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED, focused);
    987     }
    988 
    989     /**
    990      * Gets whether this node is selected.
    991      *
    992      * @return True if the node is selected.
    993      */
    994     public boolean isSelected() {
    995         return getBooleanProperty(PROPERTY_SELECTED);
    996     }
    997 
    998     /**
    999      * Sets whether this node is selected.
   1000      * <p>
   1001      *   <strong>Note:</strong> Cannot be called from an
   1002      *   {@link android.accessibilityservice.AccessibilityService}.
   1003      *   This class is made immutable before being delivered to an AccessibilityService.
   1004      * </p>
   1005      *
   1006      * @param selected True if the node is selected.
   1007      *
   1008      * @throws IllegalStateException If called from an AccessibilityService.
   1009      */
   1010     public void setSelected(boolean selected) {
   1011         setBooleanProperty(PROPERTY_SELECTED, selected);
   1012     }
   1013 
   1014     /**
   1015      * Gets whether this node is clickable.
   1016      *
   1017      * @return True if the node is clickable.
   1018      */
   1019     public boolean isClickable() {
   1020         return getBooleanProperty(PROPERTY_CLICKABLE);
   1021     }
   1022 
   1023     /**
   1024      * Sets whether this node is clickable.
   1025      * <p>
   1026      *   <strong>Note:</strong> Cannot be called from an
   1027      *   {@link android.accessibilityservice.AccessibilityService}.
   1028      *   This class is made immutable before being delivered to an AccessibilityService.
   1029      * </p>
   1030      *
   1031      * @param clickable True if the node is clickable.
   1032      *
   1033      * @throws IllegalStateException If called from an AccessibilityService.
   1034      */
   1035     public void setClickable(boolean clickable) {
   1036         setBooleanProperty(PROPERTY_CLICKABLE, clickable);
   1037     }
   1038 
   1039     /**
   1040      * Gets whether this node is long clickable.
   1041      *
   1042      * @return True if the node is long clickable.
   1043      */
   1044     public boolean isLongClickable() {
   1045         return getBooleanProperty(PROPERTY_LONG_CLICKABLE);
   1046     }
   1047 
   1048     /**
   1049      * Sets whether this node is long clickable.
   1050      * <p>
   1051      *   <strong>Note:</strong> Cannot be called from an
   1052      *   {@link android.accessibilityservice.AccessibilityService}.
   1053      *   This class is made immutable before being delivered to an AccessibilityService.
   1054      * </p>
   1055      *
   1056      * @param longClickable True if the node is long clickable.
   1057      *
   1058      * @throws IllegalStateException If called from an AccessibilityService.
   1059      */
   1060     public void setLongClickable(boolean longClickable) {
   1061         setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable);
   1062     }
   1063 
   1064     /**
   1065      * Gets whether this node is enabled.
   1066      *
   1067      * @return True if the node is enabled.
   1068      */
   1069     public boolean isEnabled() {
   1070         return getBooleanProperty(PROPERTY_ENABLED);
   1071     }
   1072 
   1073     /**
   1074      * Sets whether this node is enabled.
   1075      * <p>
   1076      *   <strong>Note:</strong> Cannot be called from an
   1077      *   {@link android.accessibilityservice.AccessibilityService}.
   1078      *   This class is made immutable before being delivered to an AccessibilityService.
   1079      * </p>
   1080      *
   1081      * @param enabled True if the node is enabled.
   1082      *
   1083      * @throws IllegalStateException If called from an AccessibilityService.
   1084      */
   1085     public void setEnabled(boolean enabled) {
   1086         setBooleanProperty(PROPERTY_ENABLED, enabled);
   1087     }
   1088 
   1089     /**
   1090      * Gets whether this node is a password.
   1091      *
   1092      * @return True if the node is a password.
   1093      */
   1094     public boolean isPassword() {
   1095         return getBooleanProperty(PROPERTY_PASSWORD);
   1096     }
   1097 
   1098     /**
   1099      * Sets whether this node is a password.
   1100      * <p>
   1101      *   <strong>Note:</strong> Cannot be called from an
   1102      *   {@link android.accessibilityservice.AccessibilityService}.
   1103      *   This class is made immutable before being delivered to an AccessibilityService.
   1104      * </p>
   1105      *
   1106      * @param password True if the node is a password.
   1107      *
   1108      * @throws IllegalStateException If called from an AccessibilityService.
   1109      */
   1110     public void setPassword(boolean password) {
   1111         setBooleanProperty(PROPERTY_PASSWORD, password);
   1112     }
   1113 
   1114     /**
   1115      * Gets if the node is scrollable.
   1116      *
   1117      * @return True if the node is scrollable, false otherwise.
   1118      */
   1119     public boolean isScrollable() {
   1120         return getBooleanProperty(PROPERTY_SCROLLABLE);
   1121     }
   1122 
   1123     /**
   1124      * Sets if the node is scrollable.
   1125      * <p>
   1126      *   <strong>Note:</strong> Cannot be called from an
   1127      *   {@link android.accessibilityservice.AccessibilityService}.
   1128      *   This class is made immutable before being delivered to an AccessibilityService.
   1129      * </p>
   1130      *
   1131      * @param scrollable True if the node is scrollable, false otherwise.
   1132      *
   1133      * @throws IllegalStateException If called from an AccessibilityService.
   1134      */
   1135     public void setScrollable(boolean scrollable) {
   1136         enforceNotSealed();
   1137         setBooleanProperty(PROPERTY_SCROLLABLE, scrollable);
   1138     }
   1139 
   1140     /**
   1141      * Gets the package this node comes from.
   1142      *
   1143      * @return The package name.
   1144      */
   1145     public CharSequence getPackageName() {
   1146         return mPackageName;
   1147     }
   1148 
   1149     /**
   1150      * Sets the package this node comes from.
   1151      * <p>
   1152      *   <strong>Note:</strong> Cannot be called from an
   1153      *   {@link android.accessibilityservice.AccessibilityService}.
   1154      *   This class is made immutable before being delivered to an AccessibilityService.
   1155      * </p>
   1156      *
   1157      * @param packageName The package name.
   1158      *
   1159      * @throws IllegalStateException If called from an AccessibilityService.
   1160      */
   1161     public void setPackageName(CharSequence packageName) {
   1162         enforceNotSealed();
   1163         mPackageName = packageName;
   1164     }
   1165 
   1166     /**
   1167      * Gets the class this node comes from.
   1168      *
   1169      * @return The class name.
   1170      */
   1171     public CharSequence getClassName() {
   1172         return mClassName;
   1173     }
   1174 
   1175     /**
   1176      * Sets the class this node comes from.
   1177      * <p>
   1178      *   <strong>Note:</strong> Cannot be called from an
   1179      *   {@link android.accessibilityservice.AccessibilityService}.
   1180      *   This class is made immutable before being delivered to an AccessibilityService.
   1181      * </p>
   1182      *
   1183      * @param className The class name.
   1184      *
   1185      * @throws IllegalStateException If called from an AccessibilityService.
   1186      */
   1187     public void setClassName(CharSequence className) {
   1188         enforceNotSealed();
   1189         mClassName = className;
   1190     }
   1191 
   1192     /**
   1193      * Gets the text of this node.
   1194      *
   1195      * @return The text.
   1196      */
   1197     public CharSequence getText() {
   1198         return mText;
   1199     }
   1200 
   1201     /**
   1202      * Sets the text of this node.
   1203      * <p>
   1204      *   <strong>Note:</strong> Cannot be called from an
   1205      *   {@link android.accessibilityservice.AccessibilityService}.
   1206      *   This class is made immutable before being delivered to an AccessibilityService.
   1207      * </p>
   1208      *
   1209      * @param text The text.
   1210      *
   1211      * @throws IllegalStateException If called from an AccessibilityService.
   1212      */
   1213     public void setText(CharSequence text) {
   1214         enforceNotSealed();
   1215         mText = text;
   1216     }
   1217 
   1218     /**
   1219      * Gets the content description of this node.
   1220      *
   1221      * @return The content description.
   1222      */
   1223     public CharSequence getContentDescription() {
   1224         return mContentDescription;
   1225     }
   1226 
   1227     /**
   1228      * Sets the content description of this node.
   1229      * <p>
   1230      *   <strong>Note:</strong> Cannot be called from an
   1231      *   {@link android.accessibilityservice.AccessibilityService}.
   1232      *   This class is made immutable before being delivered to an AccessibilityService.
   1233      * </p>
   1234      *
   1235      * @param contentDescription The content description.
   1236      *
   1237      * @throws IllegalStateException If called from an AccessibilityService.
   1238      */
   1239     public void setContentDescription(CharSequence contentDescription) {
   1240         enforceNotSealed();
   1241         mContentDescription = contentDescription;
   1242     }
   1243 
   1244     /**
   1245      * Gets the value of a boolean property.
   1246      *
   1247      * @param property The property.
   1248      * @return The value.
   1249      */
   1250     private boolean getBooleanProperty(int property) {
   1251         return (mBooleanProperties & property) != 0;
   1252     }
   1253 
   1254     /**
   1255      * Sets a boolean property.
   1256      *
   1257      * @param property The property.
   1258      * @param value The value.
   1259      *
   1260      * @throws IllegalStateException If called from an AccessibilityService.
   1261      */
   1262     private void setBooleanProperty(int property, boolean value) {
   1263         enforceNotSealed();
   1264         if (value) {
   1265             mBooleanProperties |= property;
   1266         } else {
   1267             mBooleanProperties &= ~property;
   1268         }
   1269     }
   1270 
   1271     /**
   1272      * Sets the unique id of the IAccessibilityServiceConnection over which
   1273      * this instance can send requests to the system.
   1274      *
   1275      * @param connectionId The connection id.
   1276      *
   1277      * @hide
   1278      */
   1279     public void setConnectionId(int connectionId) {
   1280         enforceNotSealed();
   1281         mConnectionId = connectionId;
   1282     }
   1283 
   1284     /**
   1285      * {@inheritDoc}
   1286      */
   1287     public int describeContents() {
   1288         return 0;
   1289     }
   1290 
   1291     /**
   1292      * Gets the id of the source node.
   1293      *
   1294      * @return The id.
   1295      *
   1296      * @hide
   1297      */
   1298     public long getSourceNodeId() {
   1299         return mSourceNodeId;
   1300     }
   1301 
   1302     /**
   1303      * Sets if this instance is sealed.
   1304      *
   1305      * @param sealed Whether is sealed.
   1306      *
   1307      * @hide
   1308      */
   1309     public void setSealed(boolean sealed) {
   1310         mSealed = sealed;
   1311     }
   1312 
   1313     /**
   1314      * Gets if this instance is sealed.
   1315      *
   1316      * @return Whether is sealed.
   1317      *
   1318      * @hide
   1319      */
   1320     public boolean isSealed() {
   1321         return mSealed;
   1322     }
   1323 
   1324     /**
   1325      * Enforces that this instance is sealed.
   1326      *
   1327      * @throws IllegalStateException If this instance is not sealed.
   1328      *
   1329      * @hide
   1330      */
   1331     protected void enforceSealed() {
   1332         if (!isSealed()) {
   1333             throw new IllegalStateException("Cannot perform this "
   1334                     + "action on a not sealed instance.");
   1335         }
   1336     }
   1337 
   1338     private void enforceValidFocusDirection(int direction) {
   1339         switch (direction) {
   1340             case View.FOCUS_DOWN:
   1341             case View.FOCUS_UP:
   1342             case View.FOCUS_LEFT:
   1343             case View.FOCUS_RIGHT:
   1344             case View.FOCUS_FORWARD:
   1345             case View.FOCUS_BACKWARD:
   1346             case View.ACCESSIBILITY_FOCUS_DOWN:
   1347             case View.ACCESSIBILITY_FOCUS_UP:
   1348             case View.ACCESSIBILITY_FOCUS_LEFT:
   1349             case View.ACCESSIBILITY_FOCUS_RIGHT:
   1350             case View.ACCESSIBILITY_FOCUS_FORWARD:
   1351             case View.ACCESSIBILITY_FOCUS_BACKWARD:
   1352                 return;
   1353             default:
   1354                 throw new IllegalArgumentException("Unknown direction: " + direction);
   1355         }
   1356     }
   1357 
   1358     private void enforceValidFocusType(int focusType) {
   1359         switch (focusType) {
   1360             case FOCUS_INPUT:
   1361             case FOCUS_ACCESSIBILITY:
   1362                 return;
   1363             default:
   1364                 throw new IllegalArgumentException("Unknown focus type: " + focusType);
   1365         }
   1366     }
   1367 
   1368     /**
   1369      * Enforces that this instance is not sealed.
   1370      *
   1371      * @throws IllegalStateException If this instance is sealed.
   1372      *
   1373      * @hide
   1374      */
   1375     protected void enforceNotSealed() {
   1376         if (isSealed()) {
   1377             throw new IllegalStateException("Cannot perform this "
   1378                     + "action on a sealed instance.");
   1379         }
   1380     }
   1381 
   1382     /**
   1383      * Returns a cached instance if such is available otherwise a new one
   1384      * and sets the source.
   1385      *
   1386      * @param source The source view.
   1387      * @return An instance.
   1388      *
   1389      * @see #setSource(View)
   1390      */
   1391     public static AccessibilityNodeInfo obtain(View source) {
   1392         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   1393         info.setSource(source);
   1394         return info;
   1395     }
   1396 
   1397     /**
   1398      * Returns a cached instance if such is available otherwise a new one
   1399      * and sets the source.
   1400      *
   1401      * @param root The root of the virtual subtree.
   1402      * @param virtualDescendantId The id of the virtual descendant.
   1403      * @return An instance.
   1404      *
   1405      * @see #setSource(View, int)
   1406      */
   1407     public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
   1408         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   1409         info.setSource(root, virtualDescendantId);
   1410         return info;
   1411     }
   1412 
   1413     /**
   1414      * Returns a cached instance if such is available otherwise a new one.
   1415      *
   1416      * @return An instance.
   1417      */
   1418     public static AccessibilityNodeInfo obtain() {
   1419         synchronized (sPoolLock) {
   1420             if (sPool != null) {
   1421                 AccessibilityNodeInfo info = sPool;
   1422                 sPool = sPool.mNext;
   1423                 sPoolSize--;
   1424                 info.mNext = null;
   1425                 info.mIsInPool = false;
   1426                 return info;
   1427             }
   1428             return new AccessibilityNodeInfo();
   1429         }
   1430     }
   1431 
   1432     /**
   1433      * Returns a cached instance if such is available or a new one is
   1434      * create. The returned instance is initialized from the given
   1435      * <code>info</code>.
   1436      *
   1437      * @param info The other info.
   1438      * @return An instance.
   1439      */
   1440     public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
   1441         AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
   1442         infoClone.init(info);
   1443         return infoClone;
   1444     }
   1445 
   1446     /**
   1447      * Return an instance back to be reused.
   1448      * <p>
   1449      * <strong>Note:</strong> You must not touch the object after calling this function.
   1450      *
   1451      * @throws IllegalStateException If the info is already recycled.
   1452      */
   1453     public void recycle() {
   1454         if (mIsInPool) {
   1455             throw new IllegalStateException("Info already recycled!");
   1456         }
   1457         clear();
   1458         synchronized (sPoolLock) {
   1459             if (sPoolSize <= MAX_POOL_SIZE) {
   1460                 mNext = sPool;
   1461                 sPool = this;
   1462                 mIsInPool = true;
   1463                 sPoolSize++;
   1464             }
   1465         }
   1466     }
   1467 
   1468     /**
   1469      * {@inheritDoc}
   1470      * <p>
   1471      *   <strong>Note:</strong> After the instance is written to a parcel it
   1472      *      is recycled. You must not touch the object after calling this function.
   1473      * </p>
   1474      */
   1475     public void writeToParcel(Parcel parcel, int flags) {
   1476         parcel.writeInt(isSealed() ? 1 : 0);
   1477         parcel.writeLong(mSourceNodeId);
   1478         parcel.writeInt(mWindowId);
   1479         parcel.writeLong(mParentNodeId);
   1480         parcel.writeInt(mConnectionId);
   1481 
   1482         SparseLongArray childIds = mChildNodeIds;
   1483         final int childIdsSize = childIds.size();
   1484         parcel.writeInt(childIdsSize);
   1485         for (int i = 0; i < childIdsSize; i++) {
   1486             parcel.writeLong(childIds.valueAt(i));
   1487         }
   1488 
   1489         parcel.writeInt(mBoundsInParent.top);
   1490         parcel.writeInt(mBoundsInParent.bottom);
   1491         parcel.writeInt(mBoundsInParent.left);
   1492         parcel.writeInt(mBoundsInParent.right);
   1493 
   1494         parcel.writeInt(mBoundsInScreen.top);
   1495         parcel.writeInt(mBoundsInScreen.bottom);
   1496         parcel.writeInt(mBoundsInScreen.left);
   1497         parcel.writeInt(mBoundsInScreen.right);
   1498 
   1499         parcel.writeInt(mActions);
   1500 
   1501         parcel.writeInt(mMovementGranularities);
   1502 
   1503         parcel.writeInt(mBooleanProperties);
   1504 
   1505         parcel.writeCharSequence(mPackageName);
   1506         parcel.writeCharSequence(mClassName);
   1507         parcel.writeCharSequence(mText);
   1508         parcel.writeCharSequence(mContentDescription);
   1509 
   1510         // Since instances of this class are fetched via synchronous i.e. blocking
   1511         // calls in IPCs we always recycle as soon as the instance is marshaled.
   1512         recycle();
   1513     }
   1514 
   1515     /**
   1516      * Initializes this instance from another one.
   1517      *
   1518      * @param other The other instance.
   1519      */
   1520     @SuppressWarnings("unchecked")
   1521     private void init(AccessibilityNodeInfo other) {
   1522         mSealed = other.mSealed;
   1523         mSourceNodeId = other.mSourceNodeId;
   1524         mParentNodeId = other.mParentNodeId;
   1525         mWindowId = other.mWindowId;
   1526         mConnectionId = other.mConnectionId;
   1527         mBoundsInParent.set(other.mBoundsInParent);
   1528         mBoundsInScreen.set(other.mBoundsInScreen);
   1529         mPackageName = other.mPackageName;
   1530         mClassName = other.mClassName;
   1531         mText = other.mText;
   1532         mContentDescription = other.mContentDescription;
   1533         mActions= other.mActions;
   1534         mBooleanProperties = other.mBooleanProperties;
   1535         mMovementGranularities = other.mMovementGranularities;
   1536         final int otherChildIdCount = other.mChildNodeIds.size();
   1537         for (int i = 0; i < otherChildIdCount; i++) {
   1538             mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));
   1539         }
   1540     }
   1541 
   1542     /**
   1543      * Creates a new instance from a {@link Parcel}.
   1544      *
   1545      * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
   1546      */
   1547     private void initFromParcel(Parcel parcel) {
   1548         mSealed = (parcel.readInt()  == 1);
   1549         mSourceNodeId = parcel.readLong();
   1550         mWindowId = parcel.readInt();
   1551         mParentNodeId = parcel.readLong();
   1552         mConnectionId = parcel.readInt();
   1553 
   1554         SparseLongArray childIds = mChildNodeIds;
   1555         final int childrenSize = parcel.readInt();
   1556         for (int i = 0; i < childrenSize; i++) {
   1557             final long childId = parcel.readLong();
   1558             childIds.put(i, childId);
   1559         }
   1560 
   1561         mBoundsInParent.top = parcel.readInt();
   1562         mBoundsInParent.bottom = parcel.readInt();
   1563         mBoundsInParent.left = parcel.readInt();
   1564         mBoundsInParent.right = parcel.readInt();
   1565 
   1566         mBoundsInScreen.top = parcel.readInt();
   1567         mBoundsInScreen.bottom = parcel.readInt();
   1568         mBoundsInScreen.left = parcel.readInt();
   1569         mBoundsInScreen.right = parcel.readInt();
   1570 
   1571         mActions = parcel.readInt();
   1572 
   1573         mMovementGranularities = parcel.readInt();
   1574 
   1575         mBooleanProperties = parcel.readInt();
   1576 
   1577         mPackageName = parcel.readCharSequence();
   1578         mClassName = parcel.readCharSequence();
   1579         mText = parcel.readCharSequence();
   1580         mContentDescription = parcel.readCharSequence();
   1581     }
   1582 
   1583     /**
   1584      * Clears the state of this instance.
   1585      */
   1586     private void clear() {
   1587         mSealed = false;
   1588         mSourceNodeId = ROOT_NODE_ID;
   1589         mParentNodeId = ROOT_NODE_ID;
   1590         mWindowId = UNDEFINED;
   1591         mConnectionId = UNDEFINED;
   1592         mMovementGranularities = 0;
   1593         mChildNodeIds.clear();
   1594         mBoundsInParent.set(0, 0, 0, 0);
   1595         mBoundsInScreen.set(0, 0, 0, 0);
   1596         mBooleanProperties = 0;
   1597         mPackageName = null;
   1598         mClassName = null;
   1599         mText = null;
   1600         mContentDescription = null;
   1601         mActions = 0;
   1602     }
   1603 
   1604     /**
   1605      * Gets the human readable action symbolic name.
   1606      *
   1607      * @param action The action.
   1608      * @return The symbolic name.
   1609      */
   1610     private static String getActionSymbolicName(int action) {
   1611         switch (action) {
   1612             case ACTION_FOCUS:
   1613                 return "ACTION_FOCUS";
   1614             case ACTION_CLEAR_FOCUS:
   1615                 return "ACTION_CLEAR_FOCUS";
   1616             case ACTION_SELECT:
   1617                 return "ACTION_SELECT";
   1618             case ACTION_CLEAR_SELECTION:
   1619                 return "ACTION_CLEAR_SELECTION";
   1620             case ACTION_CLICK:
   1621                 return "ACTION_CLICK";
   1622             case ACTION_LONG_CLICK:
   1623                 return "ACTION_LONG_CLICK";
   1624             case ACTION_ACCESSIBILITY_FOCUS:
   1625                 return "ACTION_ACCESSIBILITY_FOCUS";
   1626             case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
   1627                 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
   1628             case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
   1629                 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
   1630             case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
   1631                 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
   1632             case ACTION_NEXT_HTML_ELEMENT:
   1633                 return "ACTION_NEXT_HTML_ELEMENT";
   1634             case ACTION_PREVIOUS_HTML_ELEMENT:
   1635                 return "ACTION_PREVIOUS_HTML_ELEMENT";
   1636             case ACTION_SCROLL_FORWARD:
   1637                 return "ACTION_SCROLL_FORWARD";
   1638             case ACTION_SCROLL_BACKWARD:
   1639                 return "ACTION_SCROLL_BACKWARD";
   1640             default:
   1641                 throw new IllegalArgumentException("Unknown action: " + action);
   1642         }
   1643     }
   1644 
   1645     /**
   1646      * Gets the human readable movement granularity symbolic name.
   1647      *
   1648      * @param granularity The granularity.
   1649      * @return The symbolic name.
   1650      */
   1651     private static String getMovementGranularitySymbolicName(int granularity) {
   1652         switch (granularity) {
   1653             case MOVEMENT_GRANULARITY_CHARACTER:
   1654                 return "MOVEMENT_GRANULARITY_CHARACTER";
   1655             case MOVEMENT_GRANULARITY_WORD:
   1656                 return "MOVEMENT_GRANULARITY_WORD";
   1657             case MOVEMENT_GRANULARITY_LINE:
   1658                 return "MOVEMENT_GRANULARITY_LINE";
   1659             case MOVEMENT_GRANULARITY_PARAGRAPH:
   1660                 return "MOVEMENT_GRANULARITY_PARAGRAPH";
   1661             case MOVEMENT_GRANULARITY_PAGE:
   1662                 return "MOVEMENT_GRANULARITY_PAGE";
   1663             default:
   1664                 throw new IllegalArgumentException("Unknown movement granularity: " + granularity);
   1665         }
   1666     }
   1667 
   1668     private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
   1669         return (mWindowId != UNDEFINED
   1670                 && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
   1671                 && mConnectionId != UNDEFINED);
   1672     }
   1673 
   1674     @Override
   1675     public boolean equals(Object object) {
   1676         if (this == object) {
   1677             return true;
   1678         }
   1679         if (object == null) {
   1680             return false;
   1681         }
   1682         if (getClass() != object.getClass()) {
   1683             return false;
   1684         }
   1685         AccessibilityNodeInfo other = (AccessibilityNodeInfo) object;
   1686         if (mSourceNodeId != other.mSourceNodeId) {
   1687             return false;
   1688         }
   1689         if (mWindowId != other.mWindowId) {
   1690             return false;
   1691         }
   1692         return true;
   1693     }
   1694 
   1695     @Override
   1696     public int hashCode() {
   1697         final int prime = 31;
   1698         int result = 1;
   1699         result = prime * result + getAccessibilityViewId(mSourceNodeId);
   1700         result = prime * result + getVirtualDescendantId(mSourceNodeId);
   1701         result = prime * result + mWindowId;
   1702         return result;
   1703     }
   1704 
   1705     @Override
   1706     public String toString() {
   1707         StringBuilder builder = new StringBuilder();
   1708         builder.append(super.toString());
   1709 
   1710         if (DEBUG) {
   1711             builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
   1712             builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
   1713             builder.append("; mParentNodeId: " + mParentNodeId);
   1714 
   1715             int granularities = mMovementGranularities;
   1716             builder.append("; MovementGranularities: [");
   1717             while (granularities != 0) {
   1718                 final int granularity = 1 << Integer.numberOfTrailingZeros(granularities);
   1719                 granularities &= ~granularity;
   1720                 builder.append(getMovementGranularitySymbolicName(granularity));
   1721                 if (granularities != 0) {
   1722                     builder.append(", ");
   1723                 }
   1724             }
   1725             builder.append("]");
   1726 
   1727             SparseLongArray childIds = mChildNodeIds;
   1728             builder.append("; childAccessibilityIds: [");
   1729             for (int i = 0, count = childIds.size(); i < count; i++) {
   1730                 builder.append(childIds.valueAt(i));
   1731                 if (i < count - 1) {
   1732                     builder.append(", ");
   1733                 }
   1734             }
   1735             builder.append("]");
   1736         }
   1737 
   1738         builder.append("; boundsInParent: " + mBoundsInParent);
   1739         builder.append("; boundsInScreen: " + mBoundsInScreen);
   1740 
   1741         builder.append("; packageName: ").append(mPackageName);
   1742         builder.append("; className: ").append(mClassName);
   1743         builder.append("; text: ").append(mText);
   1744         builder.append("; contentDescription: ").append(mContentDescription);
   1745 
   1746         builder.append("; checkable: ").append(isCheckable());
   1747         builder.append("; checked: ").append(isChecked());
   1748         builder.append("; focusable: ").append(isFocusable());
   1749         builder.append("; focused: ").append(isFocused());
   1750         builder.append("; selected: ").append(isSelected());
   1751         builder.append("; clickable: ").append(isClickable());
   1752         builder.append("; longClickable: ").append(isLongClickable());
   1753         builder.append("; enabled: ").append(isEnabled());
   1754         builder.append("; password: ").append(isPassword());
   1755         builder.append("; scrollable: " + isScrollable());
   1756 
   1757         builder.append("; [");
   1758         for (int actionBits = mActions; actionBits != 0;) {
   1759             final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
   1760             actionBits &= ~action;
   1761             builder.append(getActionSymbolicName(action));
   1762             if (actionBits != 0) {
   1763                 builder.append(", ");
   1764             }
   1765         }
   1766         builder.append("]");
   1767 
   1768         return builder.toString();
   1769     }
   1770 
   1771     /**
   1772      * @see Parcelable.Creator
   1773      */
   1774     public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
   1775             new Parcelable.Creator<AccessibilityNodeInfo>() {
   1776         public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
   1777             AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   1778             info.initFromParcel(parcel);
   1779             return info;
   1780         }
   1781 
   1782         public AccessibilityNodeInfo[] newArray(int size) {
   1783             return new AccessibilityNodeInfo[size];
   1784         }
   1785     };
   1786 }
   1787