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