AccessibilityNodeInfo.java revision 27e2da7c171afa39358bbead18fbe3e6b8ea6637
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        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                return;
1347            default:
1348                throw new IllegalArgumentException("Unknown direction: " + direction);
1349        }
1350    }
1351
1352    private void enforceValidFocusType(int focusType) {
1353        switch (focusType) {
1354            case FOCUS_INPUT:
1355            case FOCUS_ACCESSIBILITY:
1356                return;
1357            default:
1358                throw new IllegalArgumentException("Unknown focus type: " + focusType);
1359        }
1360    }
1361
1362    /**
1363     * Enforces that this instance is not sealed.
1364     *
1365     * @throws IllegalStateException If this instance is sealed.
1366     *
1367     * @hide
1368     */
1369    protected void enforceNotSealed() {
1370        if (isSealed()) {
1371            throw new IllegalStateException("Cannot perform this "
1372                    + "action on a sealed instance.");
1373        }
1374    }
1375
1376    /**
1377     * Returns a cached instance if such is available otherwise a new one
1378     * and sets the source.
1379     *
1380     * @param source The source view.
1381     * @return An instance.
1382     *
1383     * @see #setSource(View)
1384     */
1385    public static AccessibilityNodeInfo obtain(View source) {
1386        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1387        info.setSource(source);
1388        return info;
1389    }
1390
1391    /**
1392     * Returns a cached instance if such is available otherwise a new one
1393     * and sets the source.
1394     *
1395     * @param root The root of the virtual subtree.
1396     * @param virtualDescendantId The id of the virtual descendant.
1397     * @return An instance.
1398     *
1399     * @see #setSource(View, int)
1400     */
1401    public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
1402        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1403        info.setSource(root, virtualDescendantId);
1404        return info;
1405    }
1406
1407    /**
1408     * Returns a cached instance if such is available otherwise a new one.
1409     *
1410     * @return An instance.
1411     */
1412    public static AccessibilityNodeInfo obtain() {
1413        synchronized (sPoolLock) {
1414            if (sPool != null) {
1415                AccessibilityNodeInfo info = sPool;
1416                sPool = sPool.mNext;
1417                sPoolSize--;
1418                info.mNext = null;
1419                info.mIsInPool = false;
1420                return info;
1421            }
1422            return new AccessibilityNodeInfo();
1423        }
1424    }
1425
1426    /**
1427     * Returns a cached instance if such is available or a new one is
1428     * create. The returned instance is initialized from the given
1429     * <code>info</code>.
1430     *
1431     * @param info The other info.
1432     * @return An instance.
1433     */
1434    public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
1435        AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
1436        infoClone.init(info);
1437        return infoClone;
1438    }
1439
1440    /**
1441     * Return an instance back to be reused.
1442     * <p>
1443     * <strong>Note:</strong> You must not touch the object after calling this function.
1444     *
1445     * @throws IllegalStateException If the info is already recycled.
1446     */
1447    public void recycle() {
1448        if (mIsInPool) {
1449            throw new IllegalStateException("Info already recycled!");
1450        }
1451        clear();
1452        synchronized (sPoolLock) {
1453            if (sPoolSize <= MAX_POOL_SIZE) {
1454                mNext = sPool;
1455                sPool = this;
1456                mIsInPool = true;
1457                sPoolSize++;
1458            }
1459        }
1460    }
1461
1462    /**
1463     * {@inheritDoc}
1464     * <p>
1465     *   <strong>Note:</strong> After the instance is written to a parcel it
1466     *      is recycled. You must not touch the object after calling this function.
1467     * </p>
1468     */
1469    public void writeToParcel(Parcel parcel, int flags) {
1470        parcel.writeInt(isSealed() ? 1 : 0);
1471        parcel.writeLong(mSourceNodeId);
1472        parcel.writeInt(mWindowId);
1473        parcel.writeLong(mParentNodeId);
1474        parcel.writeInt(mConnectionId);
1475
1476        SparseLongArray childIds = mChildNodeIds;
1477        final int childIdsSize = childIds.size();
1478        parcel.writeInt(childIdsSize);
1479        for (int i = 0; i < childIdsSize; i++) {
1480            parcel.writeLong(childIds.valueAt(i));
1481        }
1482
1483        parcel.writeInt(mBoundsInParent.top);
1484        parcel.writeInt(mBoundsInParent.bottom);
1485        parcel.writeInt(mBoundsInParent.left);
1486        parcel.writeInt(mBoundsInParent.right);
1487
1488        parcel.writeInt(mBoundsInScreen.top);
1489        parcel.writeInt(mBoundsInScreen.bottom);
1490        parcel.writeInt(mBoundsInScreen.left);
1491        parcel.writeInt(mBoundsInScreen.right);
1492
1493        parcel.writeInt(mActions);
1494
1495        parcel.writeInt(mMovementGranularities);
1496
1497        parcel.writeInt(mBooleanProperties);
1498
1499        parcel.writeCharSequence(mPackageName);
1500        parcel.writeCharSequence(mClassName);
1501        parcel.writeCharSequence(mText);
1502        parcel.writeCharSequence(mContentDescription);
1503
1504        // Since instances of this class are fetched via synchronous i.e. blocking
1505        // calls in IPCs we always recycle as soon as the instance is marshaled.
1506        recycle();
1507    }
1508
1509    /**
1510     * Initializes this instance from another one.
1511     *
1512     * @param other The other instance.
1513     */
1514    @SuppressWarnings("unchecked")
1515    private void init(AccessibilityNodeInfo other) {
1516        mSealed = other.mSealed;
1517        mSourceNodeId = other.mSourceNodeId;
1518        mParentNodeId = other.mParentNodeId;
1519        mWindowId = other.mWindowId;
1520        mConnectionId = other.mConnectionId;
1521        mBoundsInParent.set(other.mBoundsInParent);
1522        mBoundsInScreen.set(other.mBoundsInScreen);
1523        mPackageName = other.mPackageName;
1524        mClassName = other.mClassName;
1525        mText = other.mText;
1526        mContentDescription = other.mContentDescription;
1527        mActions= other.mActions;
1528        mBooleanProperties = other.mBooleanProperties;
1529        mMovementGranularities = other.mMovementGranularities;
1530        final int otherChildIdCount = other.mChildNodeIds.size();
1531        for (int i = 0; i < otherChildIdCount; i++) {
1532            mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));
1533        }
1534    }
1535
1536    /**
1537     * Creates a new instance from a {@link Parcel}.
1538     *
1539     * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
1540     */
1541    private void initFromParcel(Parcel parcel) {
1542        mSealed = (parcel.readInt()  == 1);
1543        mSourceNodeId = parcel.readLong();
1544        mWindowId = parcel.readInt();
1545        mParentNodeId = parcel.readLong();
1546        mConnectionId = parcel.readInt();
1547
1548        SparseLongArray childIds = mChildNodeIds;
1549        final int childrenSize = parcel.readInt();
1550        for (int i = 0; i < childrenSize; i++) {
1551            final long childId = parcel.readLong();
1552            childIds.put(i, childId);
1553        }
1554
1555        mBoundsInParent.top = parcel.readInt();
1556        mBoundsInParent.bottom = parcel.readInt();
1557        mBoundsInParent.left = parcel.readInt();
1558        mBoundsInParent.right = parcel.readInt();
1559
1560        mBoundsInScreen.top = parcel.readInt();
1561        mBoundsInScreen.bottom = parcel.readInt();
1562        mBoundsInScreen.left = parcel.readInt();
1563        mBoundsInScreen.right = parcel.readInt();
1564
1565        mActions = parcel.readInt();
1566
1567        mMovementGranularities = parcel.readInt();
1568
1569        mBooleanProperties = parcel.readInt();
1570
1571        mPackageName = parcel.readCharSequence();
1572        mClassName = parcel.readCharSequence();
1573        mText = parcel.readCharSequence();
1574        mContentDescription = parcel.readCharSequence();
1575    }
1576
1577    /**
1578     * Clears the state of this instance.
1579     */
1580    private void clear() {
1581        mSealed = false;
1582        mSourceNodeId = ROOT_NODE_ID;
1583        mParentNodeId = ROOT_NODE_ID;
1584        mWindowId = UNDEFINED;
1585        mConnectionId = UNDEFINED;
1586        mMovementGranularities = 0;
1587        mChildNodeIds.clear();
1588        mBoundsInParent.set(0, 0, 0, 0);
1589        mBoundsInScreen.set(0, 0, 0, 0);
1590        mBooleanProperties = 0;
1591        mPackageName = null;
1592        mClassName = null;
1593        mText = null;
1594        mContentDescription = null;
1595        mActions = 0;
1596    }
1597
1598    /**
1599     * Gets the human readable action symbolic name.
1600     *
1601     * @param action The action.
1602     * @return The symbolic name.
1603     */
1604    private static String getActionSymbolicName(int action) {
1605        switch (action) {
1606            case ACTION_FOCUS:
1607                return "ACTION_FOCUS";
1608            case ACTION_CLEAR_FOCUS:
1609                return "ACTION_CLEAR_FOCUS";
1610            case ACTION_SELECT:
1611                return "ACTION_SELECT";
1612            case ACTION_CLEAR_SELECTION:
1613                return "ACTION_CLEAR_SELECTION";
1614            case ACTION_CLICK:
1615                return "ACTION_CLICK";
1616            case ACTION_LONG_CLICK:
1617                return "ACTION_LONG_CLICK";
1618            case ACTION_ACCESSIBILITY_FOCUS:
1619                return "ACTION_ACCESSIBILITY_FOCUS";
1620            case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
1621                return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
1622            case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
1623                return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
1624            case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
1625                return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
1626            case ACTION_NEXT_HTML_ELEMENT:
1627                return "ACTION_NEXT_HTML_ELEMENT";
1628            case ACTION_PREVIOUS_HTML_ELEMENT:
1629                return "ACTION_PREVIOUS_HTML_ELEMENT";
1630            case ACTION_SCROLL_FORWARD:
1631                return "ACTION_SCROLL_FORWARD";
1632            case ACTION_SCROLL_BACKWARD:
1633                return "ACTION_SCROLL_BACKWARD";
1634            default:
1635                throw new IllegalArgumentException("Unknown action: " + action);
1636        }
1637    }
1638
1639    /**
1640     * Gets the human readable movement granularity symbolic name.
1641     *
1642     * @param granularity The granularity.
1643     * @return The symbolic name.
1644     */
1645    private static String getMovementGranularitySymbolicName(int granularity) {
1646        switch (granularity) {
1647            case MOVEMENT_GRANULARITY_CHARACTER:
1648                return "MOVEMENT_GRANULARITY_CHARACTER";
1649            case MOVEMENT_GRANULARITY_WORD:
1650                return "MOVEMENT_GRANULARITY_WORD";
1651            case MOVEMENT_GRANULARITY_LINE:
1652                return "MOVEMENT_GRANULARITY_LINE";
1653            case MOVEMENT_GRANULARITY_PARAGRAPH:
1654                return "MOVEMENT_GRANULARITY_PARAGRAPH";
1655            case MOVEMENT_GRANULARITY_PAGE:
1656                return "MOVEMENT_GRANULARITY_PAGE";
1657            default:
1658                throw new IllegalArgumentException("Unknown movement granularity: " + granularity);
1659        }
1660    }
1661
1662    private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
1663        return (mWindowId != UNDEFINED
1664                && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
1665                && mConnectionId != UNDEFINED);
1666    }
1667
1668    @Override
1669    public boolean equals(Object object) {
1670        if (this == object) {
1671            return true;
1672        }
1673        if (object == null) {
1674            return false;
1675        }
1676        if (getClass() != object.getClass()) {
1677            return false;
1678        }
1679        AccessibilityNodeInfo other = (AccessibilityNodeInfo) object;
1680        if (mSourceNodeId != other.mSourceNodeId) {
1681            return false;
1682        }
1683        if (mWindowId != other.mWindowId) {
1684            return false;
1685        }
1686        return true;
1687    }
1688
1689    @Override
1690    public int hashCode() {
1691        final int prime = 31;
1692        int result = 1;
1693        result = prime * result + getAccessibilityViewId(mSourceNodeId);
1694        result = prime * result + getVirtualDescendantId(mSourceNodeId);
1695        result = prime * result + mWindowId;
1696        return result;
1697    }
1698
1699    @Override
1700    public String toString() {
1701        StringBuilder builder = new StringBuilder();
1702        builder.append(super.toString());
1703
1704        if (DEBUG) {
1705            builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
1706            builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
1707            builder.append("; mParentNodeId: " + mParentNodeId);
1708
1709            int granularities = mMovementGranularities;
1710            builder.append("; MovementGranularities: [");
1711            while (granularities != 0) {
1712                final int granularity = 1 << Integer.numberOfTrailingZeros(granularities);
1713                granularities &= ~granularity;
1714                builder.append(getMovementGranularitySymbolicName(granularity));
1715                if (granularities != 0) {
1716                    builder.append(", ");
1717                }
1718            }
1719            builder.append("]");
1720
1721            SparseLongArray childIds = mChildNodeIds;
1722            builder.append("; childAccessibilityIds: [");
1723            for (int i = 0, count = childIds.size(); i < count; i++) {
1724                builder.append(childIds.valueAt(i));
1725                if (i < count - 1) {
1726                    builder.append(", ");
1727                }
1728            }
1729            builder.append("]");
1730        }
1731
1732        builder.append("; boundsInParent: " + mBoundsInParent);
1733        builder.append("; boundsInScreen: " + mBoundsInScreen);
1734
1735        builder.append("; packageName: ").append(mPackageName);
1736        builder.append("; className: ").append(mClassName);
1737        builder.append("; text: ").append(mText);
1738        builder.append("; contentDescription: ").append(mContentDescription);
1739
1740        builder.append("; checkable: ").append(isCheckable());
1741        builder.append("; checked: ").append(isChecked());
1742        builder.append("; focusable: ").append(isFocusable());
1743        builder.append("; focused: ").append(isFocused());
1744        builder.append("; selected: ").append(isSelected());
1745        builder.append("; clickable: ").append(isClickable());
1746        builder.append("; longClickable: ").append(isLongClickable());
1747        builder.append("; enabled: ").append(isEnabled());
1748        builder.append("; password: ").append(isPassword());
1749        builder.append("; scrollable: " + isScrollable());
1750
1751        builder.append("; [");
1752        for (int actionBits = mActions; actionBits != 0;) {
1753            final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
1754            actionBits &= ~action;
1755            builder.append(getActionSymbolicName(action));
1756            if (actionBits != 0) {
1757                builder.append(", ");
1758            }
1759        }
1760        builder.append("]");
1761
1762        return builder.toString();
1763    }
1764
1765    /**
1766     * @see Parcelable.Creator
1767     */
1768    public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
1769            new Parcelable.Creator<AccessibilityNodeInfo>() {
1770        public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
1771            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1772            info.initFromParcel(parcel);
1773            return info;
1774        }
1775
1776        public AccessibilityNodeInfo[] newArray(int size) {
1777            return new AccessibilityNodeInfo[size];
1778        }
1779    };
1780}
1781