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