AccessibilityNodeInfo.java revision b7ff3255c6d4e12f9d2334e3bbec0a125b7b09dc
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 long clicks on the node info.
106     */
107    public static final int ACTION_CLICK = 0x00000010;
108
109    /**
110     * Action that 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 granularity. For example, move to the next character, word, etc.
127     * <p>
128     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_GRANULARITY_INT}<br>
129     * <strong>Example:</strong>
130     * <code><pre><p>
131     *   Bundle arguments = new Bundle();
132     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY_INT,
133     *           AccessibilityNodeInfo.GRANULARITY_CHARACTER);
134     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY, arguments);
135     * </code></pre></p>
136     * </p>
137     *
138     * @see #setGranularities(int)
139     * @see #getGranularities()
140     *
141     * @see #GRANULARITY_CHARACTER
142     * @see #GRANULARITY_WORD
143     * @see #GRANULARITY_LINE
144     * @see #GRANULARITY_PARAGRAPH
145     * @see #GRANULARITY_PAGE
146     */
147    public static final int ACTION_NEXT_AT_GRANULARITY = 0x00000100;
148
149    /**
150     * Action that requests to go to the previous entity in this node's text
151     * at a given granularity. For example, move to the next character, word, etc.
152     * <p>
153     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_GRANULARITY_INT}<br>
154     * <strong>Example:</strong>
155     * <code><pre><p>
156     *   Bundle arguments = new Bundle();
157     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY_INT,
158     *           AccessibilityNodeInfo.GRANULARITY_CHARACTER);
159     *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY, arguments);
160     * </code></pre></p>
161     * </p>
162     *
163     * @see #setGranularities(int)
164     * @see #getGranularities()
165     *
166     * @see #GRANULARITY_CHARACTER
167     * @see #GRANULARITY_WORD
168     * @see #GRANULARITY_LINE
169     * @see #GRANULARITY_PARAGRAPH
170     * @see #GRANULARITY_PAGE
171     */
172    public static final int ACTION_PREVIOUS_AT_GRANULARITY = 0x00000200;
173
174    /**
175     * Action to move to the next HTML element of a given type. For example, move
176     * to the BUTTON, INPUT, TABLE, etc.
177     * <p>
178     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
179     * <strong>Example:</strong>
180     * <code><pre><p>
181     *   Bundle arguments = new Bundle();
182     *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
183     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
184     * </code></pre></p>
185     * </p>
186     */
187    public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
188
189    /**
190     * Action to move to the previous HTML element of a given type. For example, move
191     * to the BUTTON, INPUT, TABLE, etc.
192     * <p>
193     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
194     * <strong>Example:</strong>
195     * <code><pre><p>
196     *   Bundle arguments = new Bundle();
197     *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
198     *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
199     * </code></pre></p>
200     * </p>
201     */
202    public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
203
204    /**
205     * Argument for which text granularity to be used when traversing the node text.
206     * <p>
207     * <strong>Type:</strong> int<br>
208     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_GRANULARITY},
209     * {@link #ACTION_PREVIOUS_AT_GRANULARITY}
210     * </p>
211     */
212    public static final String ACTION_ARGUMENT_GRANULARITY_INT = "ACTION_ARGUMENT_GRANULARITY_INT";
213
214    /**
215     * Argument for which HTML element to get moving to the next/previous HTML element.
216     * <p>
217     * <strong>Type:</strong> String<br>
218     * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
219     *         {@link #ACTION_PREVIOUS_HTML_ELEMENT}
220     * </p>
221     */
222    public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
223        "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
224
225    /**
226     * The input focus.
227     */
228    public static final int FOCUS_INPUT = 1;
229
230    /**
231     * The accessibility focus.
232     */
233    public static final int FOCUS_ACCESSIBILITY = 2;
234
235    // Granularities
236
237    /**
238     * Granularity bit for traversing the text of a node by character.
239     */
240    public static final int GRANULARITY_CHARACTER = 0x00000001;
241
242    /**
243     * Granularity bit for traversing the text of a node by word.
244     */
245    public static final int GRANULARITY_WORD = 0x00000002;
246
247    /**
248     * Granularity bit for traversing the text of a node by line.
249     */
250    public static final int GRANULARITY_LINE = 0x00000004;
251
252    /**
253     * Granularity bit for traversing the text of a node by paragraph.
254     */
255    public static final int GRANULARITY_PARAGRAPH = 0x00000008;
256
257    /**
258     * Granularity bit for traversing the text of a node by page.
259     */
260    public static final int GRANULARITY_PAGE = 0x00000010;
261
262    // Boolean attributes.
263
264    private static final int PROPERTY_CHECKABLE = 0x00000001;
265
266    private static final int PROPERTY_CHECKED = 0x00000002;
267
268    private static final int PROPERTY_FOCUSABLE = 0x00000004;
269
270    private static final int PROPERTY_FOCUSED = 0x00000008;
271
272    private static final int PROPERTY_SELECTED = 0x00000010;
273
274    private static final int PROPERTY_CLICKABLE = 0x00000020;
275
276    private static final int PROPERTY_LONG_CLICKABLE = 0x00000040;
277
278    private static final int PROPERTY_ENABLED = 0x00000080;
279
280    private static final int PROPERTY_PASSWORD = 0x00000100;
281
282    private static final int PROPERTY_SCROLLABLE = 0x00000200;
283
284    private static final int PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
285
286    /**
287     * Bits that provide the id of a virtual descendant of a view.
288     */
289    private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L;
290
291    /**
292     * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a
293     * virtual descendant of a view. Such a descendant does not exist in the view
294     * hierarchy and is only reported via the accessibility APIs.
295     */
296    private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
297
298    /**
299     * Gets the accessibility view id which identifies a View in the view three.
300     *
301     * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
302     * @return The accessibility view id part of the node id.
303     *
304     * @hide
305     */
306    public static int getAccessibilityViewId(long accessibilityNodeId) {
307        return (int) accessibilityNodeId;
308    }
309
310    /**
311     * Gets the virtual descendant id which identifies an imaginary view in a
312     * containing View.
313     *
314     * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
315     * @return The virtual view id part of the node id.
316     *
317     * @hide
318     */
319    public static int getVirtualDescendantId(long accessibilityNodeId) {
320        return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK)
321                >> VIRTUAL_DESCENDANT_ID_SHIFT);
322    }
323
324    /**
325     * Makes a node id by shifting the <code>virtualDescendantId</code>
326     * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking
327     * the bitwise or with the <code>accessibilityViewId</code>.
328     *
329     * @param accessibilityViewId A View accessibility id.
330     * @param virtualDescendantId A virtual descendant id.
331     * @return The node id.
332     *
333     * @hide
334     */
335    public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) {
336        return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
337    }
338
339    // Housekeeping.
340    private static final int MAX_POOL_SIZE = 50;
341    private static final Object sPoolLock = new Object();
342    private static AccessibilityNodeInfo sPool;
343    private static int sPoolSize;
344    private AccessibilityNodeInfo mNext;
345    private boolean mIsInPool;
346    private boolean mSealed;
347
348    // Data.
349    private int mWindowId = UNDEFINED;
350    private long mSourceNodeId = ROOT_NODE_ID;
351    private long mParentNodeId = ROOT_NODE_ID;
352
353    private int mBooleanProperties;
354    private final Rect mBoundsInParent = new Rect();
355    private final Rect mBoundsInScreen = new Rect();
356
357    private CharSequence mPackageName;
358    private CharSequence mClassName;
359    private CharSequence mText;
360    private CharSequence mContentDescription;
361
362    private final SparseLongArray mChildNodeIds = new SparseLongArray();
363    private int mActions;
364
365    private int mGranularities;
366
367    private int mConnectionId = UNDEFINED;
368
369    /**
370     * Hide constructor from clients.
371     */
372    private AccessibilityNodeInfo() {
373        /* do nothing */
374    }
375
376    /**
377     * Sets the source.
378     * <p>
379     *   <strong>Note:</strong> Cannot be called from an
380     *   {@link android.accessibilityservice.AccessibilityService}.
381     *   This class is made immutable before being delivered to an AccessibilityService.
382     * </p>
383     *
384     * @param source The info source.
385     */
386    public void setSource(View source) {
387        setSource(source, UNDEFINED);
388    }
389
390    /**
391     * Sets the source to be a virtual descendant of the given <code>root</code>.
392     * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
393     * is set as the source.
394     * <p>
395     * A virtual descendant is an imaginary View that is reported as a part of the view
396     * hierarchy for accessibility purposes. This enables custom views that draw complex
397     * content to report themselves as a tree of virtual views, thus conveying their
398     * logical structure.
399     * </p>
400     * <p>
401     *   <strong>Note:</strong> Cannot be called from an
402     *   {@link android.accessibilityservice.AccessibilityService}.
403     *   This class is made immutable before being delivered to an AccessibilityService.
404     * </p>
405     *
406     * @param root The root of the virtual subtree.
407     * @param virtualDescendantId The id of the virtual descendant.
408     */
409    public void setSource(View root, int virtualDescendantId) {
410        enforceNotSealed();
411        mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
412        final int rootAccessibilityViewId =
413            (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
414        mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
415    }
416
417    /**
418     * Find the view that has the specified focus type. The search starts from
419     * the view represented by this node info.
420     *
421     * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
422     *         {@link #FOCUS_ACCESSIBILITY}.
423     * @return The node info of the focused view or null.
424     *
425     * @see #FOCUS_INPUT
426     * @see #FOCUS_ACCESSIBILITY
427     */
428    public AccessibilityNodeInfo findFocus(int focus) {
429        enforceSealed();
430        if (!canPerformRequestOverConnection(mSourceNodeId)) {
431            return null;
432        }
433        return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId,
434                mSourceNodeId, focus);
435    }
436
437    /**
438     * Searches for the nearest view in the specified direction that can take
439     * the input focus.
440     *
441     * @param direction The direction. Can be one of:
442     *     {@link View#FOCUS_DOWN},
443     *     {@link View#FOCUS_UP},
444     *     {@link View#FOCUS_LEFT},
445     *     {@link View#FOCUS_RIGHT},
446     *     {@link View#FOCUS_FORWARD},
447     *     {@link View#FOCUS_BACKWARD},
448     *     {@link View#ACCESSIBILITY_FOCUS_FORWARD},
449     *     {@link View#ACCESSIBILITY_FOCUS_BACKWARD},
450     *     {@link View#ACCESSIBILITY_FOCUS_UP},
451     *     {@link View#ACCESSIBILITY_FOCUS_RIGHT},
452     *     {@link View#ACCESSIBILITY_FOCUS_DOWN},
453     *     {@link View#ACCESSIBILITY_FOCUS_LEFT}.
454     *
455     * @return The node info for the view that can take accessibility focus.
456     */
457    public AccessibilityNodeInfo focusSearch(int direction) {
458        enforceSealed();
459        if (!canPerformRequestOverConnection(mSourceNodeId)) {
460            return null;
461        }
462        return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId,
463                mSourceNodeId, direction);
464    }
465
466    /**
467     * Gets the id of the window from which the info comes from.
468     *
469     * @return The window id.
470     */
471    public int getWindowId() {
472        return mWindowId;
473    }
474
475    /**
476     * @return The ids of the children.
477     *
478     * @hide
479     */
480    public SparseLongArray getChildNodeIds() {
481        return mChildNodeIds;
482    }
483
484    /**
485     * Gets the number of children.
486     *
487     * @return The child count.
488     */
489    public int getChildCount() {
490        return mChildNodeIds.size();
491    }
492
493    /**
494     * Get the child at given index.
495     * <p>
496     *   <strong>Note:</strong> It is a client responsibility to recycle the
497     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
498     *     to avoid creating of multiple instances.
499     * </p>
500     *
501     * @param index The child index.
502     * @return The child node.
503     *
504     * @throws IllegalStateException If called outside of an AccessibilityService.
505     *
506     */
507    public AccessibilityNodeInfo getChild(int index) {
508        enforceSealed();
509        if (!canPerformRequestOverConnection(mSourceNodeId)) {
510            return null;
511        }
512        final long childId = mChildNodeIds.get(index);
513        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
514        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
515                childId, FLAG_PREFETCH_DESCENDANTS);
516    }
517
518    /**
519     * Adds a child.
520     * <p>
521     * <strong>Note:</strong> Cannot be called from an
522     * {@link android.accessibilityservice.AccessibilityService}.
523     * This class is made immutable before being delivered to an AccessibilityService.
524     * </p>
525     *
526     * @param child The child.
527     *
528     * @throws IllegalStateException If called from an AccessibilityService.
529     */
530    public void addChild(View child) {
531        addChild(child, UNDEFINED);
532    }
533
534    /**
535     * Adds a virtual child which is a descendant of the given <code>root</code>.
536     * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
537     * is added as a child.
538     * <p>
539     * A virtual descendant is an imaginary View that is reported as a part of the view
540     * hierarchy for accessibility purposes. This enables custom views that draw complex
541     * content to report them selves as a tree of virtual views, thus conveying their
542     * logical structure.
543     * </p>
544     *
545     * @param root The root of the virtual subtree.
546     * @param virtualDescendantId The id of the virtual child.
547     */
548    public void addChild(View root, int virtualDescendantId) {
549        enforceNotSealed();
550        final int index = mChildNodeIds.size();
551        final int rootAccessibilityViewId =
552            (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
553        final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
554        mChildNodeIds.put(index, childNodeId);
555    }
556
557    /**
558     * Gets the actions that can be performed on the node.
559     *
560     * @return The bit mask of with actions.
561     *
562     * @see AccessibilityNodeInfo#ACTION_FOCUS
563     * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
564     * @see AccessibilityNodeInfo#ACTION_SELECT
565     * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
566     */
567    public int getActions() {
568        return mActions;
569    }
570
571    /**
572     * Adds an action that can be performed on the node.
573     * <p>
574     *   <strong>Note:</strong> Cannot be called from an
575     *   {@link android.accessibilityservice.AccessibilityService}.
576     *   This class is made immutable before being delivered to an AccessibilityService.
577     * </p>
578     *
579     * @param action The action.
580     *
581     * @throws IllegalStateException If called from an AccessibilityService.
582     */
583    public void addAction(int action) {
584        enforceNotSealed();
585        mActions |= action;
586    }
587
588    /**
589     * Sets the text granularities for traversing the text of this node.
590     * <p>
591     *   <strong>Note:</strong> Cannot be called from an
592     *   {@link android.accessibilityservice.AccessibilityService}.
593     *   This class is made immutable before being delivered to an AccessibilityService.
594     * </p>
595     *
596     * @param granularities The bit mask with granularities.
597     *
598     * @throws IllegalStateException If called from an AccessibilityService.
599     */
600    public void setGranularities(int granularities) {
601        enforceNotSealed();
602        mGranularities = granularities;
603    }
604
605    /**
606     * Gets the granularities for traversing the text of this node.
607     *
608     * @return The bit mask with granularities.
609     */
610    public int getGranularities() {
611        return mGranularities;
612    }
613
614    /**
615     * Performs an action on the node.
616     * <p>
617     *   <strong>Note:</strong> An action can be performed only if the request is made
618     *   from an {@link android.accessibilityservice.AccessibilityService}.
619     * </p>
620     *
621     * @param action The action to perform.
622     * @return True if the action was performed.
623     *
624     * @throws IllegalStateException If called outside of an AccessibilityService.
625     */
626    public boolean performAction(int action) {
627        enforceSealed();
628        if (!canPerformRequestOverConnection(mSourceNodeId)) {
629            return false;
630        }
631        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
632        return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
633                action, null);
634    }
635
636    /**
637     * Performs an action on the node.
638     * <p>
639     *   <strong>Note:</strong> An action can be performed only if the request is made
640     *   from an {@link android.accessibilityservice.AccessibilityService}.
641     * </p>
642     *
643     * @param action The action to perform.
644     * @param arguments A bundle with additional arguments.
645     * @return True if the action was performed.
646     *
647     * @throws IllegalStateException If called outside of an AccessibilityService.
648     */
649    public boolean performAction(int action, Bundle arguments) {
650        enforceSealed();
651        if (!canPerformRequestOverConnection(mSourceNodeId)) {
652            return false;
653        }
654        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
655        return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
656                action, arguments);
657    }
658
659    /**
660     * Finds {@link AccessibilityNodeInfo}s by text. The match is case
661     * insensitive containment. The search is relative to this info i.e.
662     * this info is the root of the traversed tree.
663     *
664     * <p>
665     *   <strong>Note:</strong> It is a client responsibility to recycle the
666     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
667     *     to avoid creating of multiple instances.
668     * </p>
669     *
670     * @param text The searched text.
671     * @return A list of node info.
672     */
673    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
674        enforceSealed();
675        if (!canPerformRequestOverConnection(mSourceNodeId)) {
676            return Collections.emptyList();
677        }
678        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
679        return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
680                text);
681    }
682
683    /**
684     * Gets the parent.
685     * <p>
686     *   <strong>Note:</strong> It is a client responsibility to recycle the
687     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
688     *     to avoid creating of multiple instances.
689     * </p>
690     *
691     * @return The parent.
692     */
693    public AccessibilityNodeInfo getParent() {
694        enforceSealed();
695        if (!canPerformRequestOverConnection(mParentNodeId)) {
696            return null;
697        }
698        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
699        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
700                mWindowId, mParentNodeId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
701    }
702
703    /**
704     * @return The parent node id.
705     *
706     * @hide
707     */
708    public long getParentNodeId() {
709        return mParentNodeId;
710    }
711
712    /**
713     * Sets the parent.
714     * <p>
715     *   <strong>Note:</strong> Cannot be called from an
716     *   {@link android.accessibilityservice.AccessibilityService}.
717     *   This class is made immutable before being delivered to an AccessibilityService.
718     * </p>
719     *
720     * @param parent The parent.
721     *
722     * @throws IllegalStateException If called from an AccessibilityService.
723     */
724    public void setParent(View parent) {
725        setParent(parent, UNDEFINED);
726    }
727
728    /**
729     * Sets the parent to be a virtual descendant of the given <code>root</code>.
730     * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
731     * is set as the parent.
732     * <p>
733     * A virtual descendant is an imaginary View that is reported as a part of the view
734     * hierarchy for accessibility purposes. This enables custom views that draw complex
735     * content to report them selves as a tree of virtual views, thus conveying their
736     * logical structure.
737     * </p>
738     * <p>
739     *   <strong>Note:</strong> Cannot be called from an
740     *   {@link android.accessibilityservice.AccessibilityService}.
741     *   This class is made immutable before being delivered to an AccessibilityService.
742     * </p>
743     *
744     * @param root The root of the virtual subtree.
745     * @param virtualDescendantId The id of the virtual descendant.
746     */
747    public void setParent(View root, int virtualDescendantId) {
748        enforceNotSealed();
749        final int rootAccessibilityViewId =
750            (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
751        mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
752    }
753
754    /**
755     * Gets the node bounds in parent coordinates.
756     *
757     * @param outBounds The output node bounds.
758     */
759    public void getBoundsInParent(Rect outBounds) {
760        outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
761                mBoundsInParent.right, mBoundsInParent.bottom);
762    }
763
764    /**
765     * Sets the node bounds in parent coordinates.
766     * <p>
767     *   <strong>Note:</strong> Cannot be called from an
768     *   {@link android.accessibilityservice.AccessibilityService}.
769     *   This class is made immutable before being delivered to an AccessibilityService.
770     * </p>
771     *
772     * @param bounds The node bounds.
773     *
774     * @throws IllegalStateException If called from an AccessibilityService.
775     */
776    public void setBoundsInParent(Rect bounds) {
777        enforceNotSealed();
778        mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
779    }
780
781    /**
782     * Gets the node bounds in screen coordinates.
783     *
784     * @param outBounds The output node bounds.
785     */
786    public void getBoundsInScreen(Rect outBounds) {
787        outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top,
788                mBoundsInScreen.right, mBoundsInScreen.bottom);
789    }
790
791    /**
792     * Sets the node bounds in screen coordinates.
793     * <p>
794     *   <strong>Note:</strong> Cannot be called from an
795     *   {@link android.accessibilityservice.AccessibilityService}.
796     *   This class is made immutable before being delivered to an AccessibilityService.
797     * </p>
798     *
799     * @param bounds The node bounds.
800     *
801     * @throws IllegalStateException If called from an AccessibilityService.
802     */
803    public void setBoundsInScreen(Rect bounds) {
804        enforceNotSealed();
805        mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
806    }
807
808    /**
809     * Gets whether this node is checkable.
810     *
811     * @return True if the node is checkable.
812     */
813    public boolean isCheckable() {
814        return getBooleanProperty(PROPERTY_CHECKABLE);
815    }
816
817    /**
818     * Sets whether this node is checkable.
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 checkable True if the node is checkable.
826     *
827     * @throws IllegalStateException If called from an AccessibilityService.
828     */
829    public void setCheckable(boolean checkable) {
830        setBooleanProperty(PROPERTY_CHECKABLE, checkable);
831    }
832
833    /**
834     * Gets whether this node is checked.
835     *
836     * @return True if the node is checked.
837     */
838    public boolean isChecked() {
839        return getBooleanProperty(PROPERTY_CHECKED);
840    }
841
842    /**
843     * Sets whether this node is checked.
844     * <p>
845     *   <strong>Note:</strong> Cannot be called from an
846     *   {@link android.accessibilityservice.AccessibilityService}.
847     *   This class is made immutable before being delivered to an AccessibilityService.
848     * </p>
849     *
850     * @param checked True if the node is checked.
851     *
852     * @throws IllegalStateException If called from an AccessibilityService.
853     */
854    public void setChecked(boolean checked) {
855        setBooleanProperty(PROPERTY_CHECKED, checked);
856    }
857
858    /**
859     * Gets whether this node is focusable.
860     *
861     * @return True if the node is focusable.
862     */
863    public boolean isFocusable() {
864        return getBooleanProperty(PROPERTY_FOCUSABLE);
865    }
866
867    /**
868     * Sets whether this node is focusable.
869     * <p>
870     *   <strong>Note:</strong> Cannot be called from an
871     *   {@link android.accessibilityservice.AccessibilityService}.
872     *   This class is made immutable before being delivered to an AccessibilityService.
873     * </p>
874     *
875     * @param focusable True if the node is focusable.
876     *
877     * @throws IllegalStateException If called from an AccessibilityService.
878     */
879    public void setFocusable(boolean focusable) {
880        setBooleanProperty(PROPERTY_FOCUSABLE, focusable);
881    }
882
883    /**
884     * Gets whether this node is focused.
885     *
886     * @return True if the node is focused.
887     */
888    public boolean isFocused() {
889        return getBooleanProperty(PROPERTY_FOCUSED);
890    }
891
892    /**
893     * Sets whether this node is focused.
894     * <p>
895     *   <strong>Note:</strong> Cannot be called from an
896     *   {@link android.accessibilityservice.AccessibilityService}.
897     *   This class is made immutable before being delivered to an AccessibilityService.
898     * </p>
899     *
900     * @param focused True if the node is focused.
901     *
902     * @throws IllegalStateException If called from an AccessibilityService.
903     */
904    public void setFocused(boolean focused) {
905        setBooleanProperty(PROPERTY_FOCUSED, focused);
906    }
907
908    /**
909     * Gets whether this node is accessibility focused.
910     *
911     * @return True if the node is accessibility focused.
912     */
913    public boolean isAccessibilityFocused() {
914        return getBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED);
915    }
916
917    /**
918     * Sets whether this node is accessibility focused.
919     * <p>
920     *   <strong>Note:</strong> Cannot be called from an
921     *   {@link android.accessibilityservice.AccessibilityService}.
922     *   This class is made immutable before being delivered to an AccessibilityService.
923     * </p>
924     *
925     * @param focused True if the node is accessibility focused.
926     *
927     * @throws IllegalStateException If called from an AccessibilityService.
928     */
929    public void setAccessibilityFocused(boolean focused) {
930        setBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED, focused);
931    }
932
933    /**
934     * Gets whether this node is selected.
935     *
936     * @return True if the node is selected.
937     */
938    public boolean isSelected() {
939        return getBooleanProperty(PROPERTY_SELECTED);
940    }
941
942    /**
943     * Sets whether this node is selected.
944     * <p>
945     *   <strong>Note:</strong> Cannot be called from an
946     *   {@link android.accessibilityservice.AccessibilityService}.
947     *   This class is made immutable before being delivered to an AccessibilityService.
948     * </p>
949     *
950     * @param selected True if the node is selected.
951     *
952     * @throws IllegalStateException If called from an AccessibilityService.
953     */
954    public void setSelected(boolean selected) {
955        setBooleanProperty(PROPERTY_SELECTED, selected);
956    }
957
958    /**
959     * Gets whether this node is clickable.
960     *
961     * @return True if the node is clickable.
962     */
963    public boolean isClickable() {
964        return getBooleanProperty(PROPERTY_CLICKABLE);
965    }
966
967    /**
968     * Sets whether this node is clickable.
969     * <p>
970     *   <strong>Note:</strong> Cannot be called from an
971     *   {@link android.accessibilityservice.AccessibilityService}.
972     *   This class is made immutable before being delivered to an AccessibilityService.
973     * </p>
974     *
975     * @param clickable True if the node is clickable.
976     *
977     * @throws IllegalStateException If called from an AccessibilityService.
978     */
979    public void setClickable(boolean clickable) {
980        setBooleanProperty(PROPERTY_CLICKABLE, clickable);
981    }
982
983    /**
984     * Gets whether this node is long clickable.
985     *
986     * @return True if the node is long clickable.
987     */
988    public boolean isLongClickable() {
989        return getBooleanProperty(PROPERTY_LONG_CLICKABLE);
990    }
991
992    /**
993     * Sets whether this node is long clickable.
994     * <p>
995     *   <strong>Note:</strong> Cannot be called from an
996     *   {@link android.accessibilityservice.AccessibilityService}.
997     *   This class is made immutable before being delivered to an AccessibilityService.
998     * </p>
999     *
1000     * @param longClickable True if the node is long clickable.
1001     *
1002     * @throws IllegalStateException If called from an AccessibilityService.
1003     */
1004    public void setLongClickable(boolean longClickable) {
1005        setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable);
1006    }
1007
1008    /**
1009     * Gets whether this node is enabled.
1010     *
1011     * @return True if the node is enabled.
1012     */
1013    public boolean isEnabled() {
1014        return getBooleanProperty(PROPERTY_ENABLED);
1015    }
1016
1017    /**
1018     * Sets whether this node is enabled.
1019     * <p>
1020     *   <strong>Note:</strong> Cannot be called from an
1021     *   {@link android.accessibilityservice.AccessibilityService}.
1022     *   This class is made immutable before being delivered to an AccessibilityService.
1023     * </p>
1024     *
1025     * @param enabled True if the node is enabled.
1026     *
1027     * @throws IllegalStateException If called from an AccessibilityService.
1028     */
1029    public void setEnabled(boolean enabled) {
1030        setBooleanProperty(PROPERTY_ENABLED, enabled);
1031    }
1032
1033    /**
1034     * Gets whether this node is a password.
1035     *
1036     * @return True if the node is a password.
1037     */
1038    public boolean isPassword() {
1039        return getBooleanProperty(PROPERTY_PASSWORD);
1040    }
1041
1042    /**
1043     * Sets whether this node is a password.
1044     * <p>
1045     *   <strong>Note:</strong> Cannot be called from an
1046     *   {@link android.accessibilityservice.AccessibilityService}.
1047     *   This class is made immutable before being delivered to an AccessibilityService.
1048     * </p>
1049     *
1050     * @param password True if the node is a password.
1051     *
1052     * @throws IllegalStateException If called from an AccessibilityService.
1053     */
1054    public void setPassword(boolean password) {
1055        setBooleanProperty(PROPERTY_PASSWORD, password);
1056    }
1057
1058    /**
1059     * Gets if the node is scrollable.
1060     *
1061     * @return True if the node is scrollable, false otherwise.
1062     */
1063    public boolean isScrollable() {
1064        return getBooleanProperty(PROPERTY_SCROLLABLE);
1065    }
1066
1067    /**
1068     * Sets if the node is scrollable.
1069     * <p>
1070     *   <strong>Note:</strong> Cannot be called from an
1071     *   {@link android.accessibilityservice.AccessibilityService}.
1072     *   This class is made immutable before being delivered to an AccessibilityService.
1073     * </p>
1074     *
1075     * @param scrollable True if the node is scrollable, false otherwise.
1076     *
1077     * @throws IllegalStateException If called from an AccessibilityService.
1078     */
1079    public void setScrollable(boolean scrollable) {
1080        enforceNotSealed();
1081        setBooleanProperty(PROPERTY_SCROLLABLE, scrollable);
1082    }
1083
1084    /**
1085     * Gets the package this node comes from.
1086     *
1087     * @return The package name.
1088     */
1089    public CharSequence getPackageName() {
1090        return mPackageName;
1091    }
1092
1093    /**
1094     * Sets the package this node comes from.
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 packageName The package name.
1102     *
1103     * @throws IllegalStateException If called from an AccessibilityService.
1104     */
1105    public void setPackageName(CharSequence packageName) {
1106        enforceNotSealed();
1107        mPackageName = packageName;
1108    }
1109
1110    /**
1111     * Gets the class this node comes from.
1112     *
1113     * @return The class name.
1114     */
1115    public CharSequence getClassName() {
1116        return mClassName;
1117    }
1118
1119    /**
1120     * Sets the class this node comes from.
1121     * <p>
1122     *   <strong>Note:</strong> Cannot be called from an
1123     *   {@link android.accessibilityservice.AccessibilityService}.
1124     *   This class is made immutable before being delivered to an AccessibilityService.
1125     * </p>
1126     *
1127     * @param className The class name.
1128     *
1129     * @throws IllegalStateException If called from an AccessibilityService.
1130     */
1131    public void setClassName(CharSequence className) {
1132        enforceNotSealed();
1133        mClassName = className;
1134    }
1135
1136    /**
1137     * Gets the text of this node.
1138     *
1139     * @return The text.
1140     */
1141    public CharSequence getText() {
1142        return mText;
1143    }
1144
1145    /**
1146     * Sets the text of this node.
1147     * <p>
1148     *   <strong>Note:</strong> Cannot be called from an
1149     *   {@link android.accessibilityservice.AccessibilityService}.
1150     *   This class is made immutable before being delivered to an AccessibilityService.
1151     * </p>
1152     *
1153     * @param text The text.
1154     *
1155     * @throws IllegalStateException If called from an AccessibilityService.
1156     */
1157    public void setText(CharSequence text) {
1158        enforceNotSealed();
1159        mText = text;
1160    }
1161
1162    /**
1163     * Gets the content description of this node.
1164     *
1165     * @return The content description.
1166     */
1167    public CharSequence getContentDescription() {
1168        return mContentDescription;
1169    }
1170
1171    /**
1172     * Sets the content description of this node.
1173     * <p>
1174     *   <strong>Note:</strong> Cannot be called from an
1175     *   {@link android.accessibilityservice.AccessibilityService}.
1176     *   This class is made immutable before being delivered to an AccessibilityService.
1177     * </p>
1178     *
1179     * @param contentDescription The content description.
1180     *
1181     * @throws IllegalStateException If called from an AccessibilityService.
1182     */
1183    public void setContentDescription(CharSequence contentDescription) {
1184        enforceNotSealed();
1185        mContentDescription = contentDescription;
1186    }
1187
1188    /**
1189     * Gets the value of a boolean property.
1190     *
1191     * @param property The property.
1192     * @return The value.
1193     */
1194    private boolean getBooleanProperty(int property) {
1195        return (mBooleanProperties & property) != 0;
1196    }
1197
1198    /**
1199     * Sets a boolean property.
1200     *
1201     * @param property The property.
1202     * @param value The value.
1203     *
1204     * @throws IllegalStateException If called from an AccessibilityService.
1205     */
1206    private void setBooleanProperty(int property, boolean value) {
1207        enforceNotSealed();
1208        if (value) {
1209            mBooleanProperties |= property;
1210        } else {
1211            mBooleanProperties &= ~property;
1212        }
1213    }
1214
1215    /**
1216     * Sets the unique id of the IAccessibilityServiceConnection over which
1217     * this instance can send requests to the system.
1218     *
1219     * @param connectionId The connection id.
1220     *
1221     * @hide
1222     */
1223    public void setConnectionId(int connectionId) {
1224        enforceNotSealed();
1225        mConnectionId = connectionId;
1226    }
1227
1228    /**
1229     * {@inheritDoc}
1230     */
1231    public int describeContents() {
1232        return 0;
1233    }
1234
1235    /**
1236     * Gets the id of the source node.
1237     *
1238     * @return The id.
1239     *
1240     * @hide
1241     */
1242    public long getSourceNodeId() {
1243        return mSourceNodeId;
1244    }
1245
1246    /**
1247     * Sets if this instance is sealed.
1248     *
1249     * @param sealed Whether is sealed.
1250     *
1251     * @hide
1252     */
1253    public void setSealed(boolean sealed) {
1254        mSealed = sealed;
1255    }
1256
1257    /**
1258     * Gets if this instance is sealed.
1259     *
1260     * @return Whether is sealed.
1261     *
1262     * @hide
1263     */
1264    public boolean isSealed() {
1265        return mSealed;
1266    }
1267
1268    /**
1269     * Enforces that this instance is sealed.
1270     *
1271     * @throws IllegalStateException If this instance is not sealed.
1272     *
1273     * @hide
1274     */
1275    protected void enforceSealed() {
1276        if (!isSealed()) {
1277            throw new IllegalStateException("Cannot perform this "
1278                    + "action on a not sealed instance.");
1279        }
1280    }
1281
1282    /**
1283     * Enforces that this instance is not sealed.
1284     *
1285     * @throws IllegalStateException If this instance is sealed.
1286     *
1287     * @hide
1288     */
1289    protected void enforceNotSealed() {
1290        if (isSealed()) {
1291            throw new IllegalStateException("Cannot perform this "
1292                    + "action on a sealed instance.");
1293        }
1294    }
1295
1296    /**
1297     * Returns a cached instance if such is available otherwise a new one
1298     * and sets the source.
1299     *
1300     * @param source The source view.
1301     * @return An instance.
1302     *
1303     * @see #setSource(View)
1304     */
1305    public static AccessibilityNodeInfo obtain(View source) {
1306        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1307        info.setSource(source);
1308        return info;
1309    }
1310
1311    /**
1312     * Returns a cached instance if such is available otherwise a new one
1313     * and sets the source.
1314     *
1315     * @param root The root of the virtual subtree.
1316     * @param virtualDescendantId The id of the virtual descendant.
1317     * @return An instance.
1318     *
1319     * @see #setSource(View, int)
1320     */
1321    public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
1322        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1323        info.setSource(root, virtualDescendantId);
1324        return info;
1325    }
1326
1327    /**
1328     * Returns a cached instance if such is available otherwise a new one.
1329     *
1330     * @return An instance.
1331     */
1332    public static AccessibilityNodeInfo obtain() {
1333        synchronized (sPoolLock) {
1334            if (sPool != null) {
1335                AccessibilityNodeInfo info = sPool;
1336                sPool = sPool.mNext;
1337                sPoolSize--;
1338                info.mNext = null;
1339                info.mIsInPool = false;
1340                return info;
1341            }
1342            return new AccessibilityNodeInfo();
1343        }
1344    }
1345
1346    /**
1347     * Returns a cached instance if such is available or a new one is
1348     * create. The returned instance is initialized from the given
1349     * <code>info</code>.
1350     *
1351     * @param info The other info.
1352     * @return An instance.
1353     */
1354    public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
1355        AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
1356        infoClone.init(info);
1357        return infoClone;
1358    }
1359
1360    /**
1361     * Return an instance back to be reused.
1362     * <p>
1363     * <strong>Note:</strong> You must not touch the object after calling this function.
1364     *
1365     * @throws IllegalStateException If the info is already recycled.
1366     */
1367    public void recycle() {
1368        if (mIsInPool) {
1369            throw new IllegalStateException("Info already recycled!");
1370        }
1371        clear();
1372        synchronized (sPoolLock) {
1373            if (sPoolSize <= MAX_POOL_SIZE) {
1374                mNext = sPool;
1375                sPool = this;
1376                mIsInPool = true;
1377                sPoolSize++;
1378            }
1379        }
1380    }
1381
1382    /**
1383     * {@inheritDoc}
1384     * <p>
1385     *   <strong>Note:</strong> After the instance is written to a parcel it
1386     *      is recycled. You must not touch the object after calling this function.
1387     * </p>
1388     */
1389    public void writeToParcel(Parcel parcel, int flags) {
1390        parcel.writeInt(isSealed() ? 1 : 0);
1391        parcel.writeLong(mSourceNodeId);
1392        parcel.writeInt(mWindowId);
1393        parcel.writeLong(mParentNodeId);
1394        parcel.writeInt(mConnectionId);
1395
1396        SparseLongArray childIds = mChildNodeIds;
1397        final int childIdsSize = childIds.size();
1398        parcel.writeInt(childIdsSize);
1399        for (int i = 0; i < childIdsSize; i++) {
1400            parcel.writeLong(childIds.valueAt(i));
1401        }
1402
1403        parcel.writeInt(mBoundsInParent.top);
1404        parcel.writeInt(mBoundsInParent.bottom);
1405        parcel.writeInt(mBoundsInParent.left);
1406        parcel.writeInt(mBoundsInParent.right);
1407
1408        parcel.writeInt(mBoundsInScreen.top);
1409        parcel.writeInt(mBoundsInScreen.bottom);
1410        parcel.writeInt(mBoundsInScreen.left);
1411        parcel.writeInt(mBoundsInScreen.right);
1412
1413        parcel.writeInt(mActions);
1414
1415        parcel.writeInt(mGranularities);
1416
1417        parcel.writeInt(mBooleanProperties);
1418
1419        parcel.writeCharSequence(mPackageName);
1420        parcel.writeCharSequence(mClassName);
1421        parcel.writeCharSequence(mText);
1422        parcel.writeCharSequence(mContentDescription);
1423
1424        // Since instances of this class are fetched via synchronous i.e. blocking
1425        // calls in IPCs we always recycle as soon as the instance is marshaled.
1426        recycle();
1427    }
1428
1429    /**
1430     * Initializes this instance from another one.
1431     *
1432     * @param other The other instance.
1433     */
1434    @SuppressWarnings("unchecked")
1435    private void init(AccessibilityNodeInfo other) {
1436        mSealed = other.mSealed;
1437        mSourceNodeId = other.mSourceNodeId;
1438        mParentNodeId = other.mParentNodeId;
1439        mWindowId = other.mWindowId;
1440        mConnectionId = other.mConnectionId;
1441        mBoundsInParent.set(other.mBoundsInParent);
1442        mBoundsInScreen.set(other.mBoundsInScreen);
1443        mPackageName = other.mPackageName;
1444        mClassName = other.mClassName;
1445        mText = other.mText;
1446        mContentDescription = other.mContentDescription;
1447        mActions= other.mActions;
1448        mBooleanProperties = other.mBooleanProperties;
1449        mGranularities = other.mGranularities;
1450        final int otherChildIdCount = other.mChildNodeIds.size();
1451        for (int i = 0; i < otherChildIdCount; i++) {
1452            mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));
1453        }
1454    }
1455
1456    /**
1457     * Creates a new instance from a {@link Parcel}.
1458     *
1459     * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
1460     */
1461    private void initFromParcel(Parcel parcel) {
1462        mSealed = (parcel.readInt()  == 1);
1463        mSourceNodeId = parcel.readLong();
1464        mWindowId = parcel.readInt();
1465        mParentNodeId = parcel.readLong();
1466        mConnectionId = parcel.readInt();
1467
1468        SparseLongArray childIds = mChildNodeIds;
1469        final int childrenSize = parcel.readInt();
1470        for (int i = 0; i < childrenSize; i++) {
1471            final long childId = parcel.readLong();
1472            childIds.put(i, childId);
1473        }
1474
1475        mBoundsInParent.top = parcel.readInt();
1476        mBoundsInParent.bottom = parcel.readInt();
1477        mBoundsInParent.left = parcel.readInt();
1478        mBoundsInParent.right = parcel.readInt();
1479
1480        mBoundsInScreen.top = parcel.readInt();
1481        mBoundsInScreen.bottom = parcel.readInt();
1482        mBoundsInScreen.left = parcel.readInt();
1483        mBoundsInScreen.right = parcel.readInt();
1484
1485        mActions = parcel.readInt();
1486
1487        mGranularities = parcel.readInt();
1488
1489        mBooleanProperties = parcel.readInt();
1490
1491        mPackageName = parcel.readCharSequence();
1492        mClassName = parcel.readCharSequence();
1493        mText = parcel.readCharSequence();
1494        mContentDescription = parcel.readCharSequence();
1495    }
1496
1497    /**
1498     * Clears the state of this instance.
1499     */
1500    private void clear() {
1501        mSealed = false;
1502        mSourceNodeId = ROOT_NODE_ID;
1503        mParentNodeId = ROOT_NODE_ID;
1504        mWindowId = UNDEFINED;
1505        mConnectionId = UNDEFINED;
1506        mGranularities = 0;
1507        mChildNodeIds.clear();
1508        mBoundsInParent.set(0, 0, 0, 0);
1509        mBoundsInScreen.set(0, 0, 0, 0);
1510        mBooleanProperties = 0;
1511        mPackageName = null;
1512        mClassName = null;
1513        mText = null;
1514        mContentDescription = null;
1515        mActions = 0;
1516    }
1517
1518    /**
1519     * Gets the human readable action symbolic name.
1520     *
1521     * @param action The action.
1522     * @return The symbolic name.
1523     */
1524    private static String getActionSymbolicName(int action) {
1525        switch (action) {
1526            case ACTION_FOCUS:
1527                return "ACTION_FOCUS";
1528            case ACTION_CLEAR_FOCUS:
1529                return "ACTION_CLEAR_FOCUS";
1530            case ACTION_SELECT:
1531                return "ACTION_SELECT";
1532            case ACTION_CLEAR_SELECTION:
1533                return "ACTION_CLEAR_SELECTION";
1534            default:
1535                throw new IllegalArgumentException("Unknown action: " + action);
1536        }
1537    }
1538
1539    /**
1540     * Gets the human readable granularity symbolic name.
1541     *
1542     * @param granularity The action.
1543     * @return The symbolic name.
1544     */
1545    private static String getGranularitySymbolicName(int granularity) {
1546        switch (granularity) {
1547            case GRANULARITY_CHARACTER:
1548                return "GRANULARITY_CHARACTER";
1549            case GRANULARITY_WORD:
1550                return "GRANULARITY_WORD";
1551            case GRANULARITY_LINE:
1552                return "GRANULARITY_LINE";
1553            case GRANULARITY_PARAGRAPH:
1554                return "GRANULARITY_PARAGRAPH";
1555            case GRANULARITY_PAGE:
1556                return "GRANULARITY_PAGE";
1557            default:
1558                throw new IllegalArgumentException("Unknown granularity: " + granularity);
1559        }
1560    }
1561
1562    private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
1563        return (mWindowId != UNDEFINED
1564                && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
1565                && mConnectionId != UNDEFINED);
1566    }
1567
1568    @Override
1569    public boolean equals(Object object) {
1570        if (this == object) {
1571            return true;
1572        }
1573        if (object == null) {
1574            return false;
1575        }
1576        if (getClass() != object.getClass()) {
1577            return false;
1578        }
1579        AccessibilityNodeInfo other = (AccessibilityNodeInfo) object;
1580        if (mSourceNodeId != other.mSourceNodeId) {
1581            return false;
1582        }
1583        if (mWindowId != other.mWindowId) {
1584            return false;
1585        }
1586        return true;
1587    }
1588
1589    @Override
1590    public int hashCode() {
1591        final int prime = 31;
1592        int result = 1;
1593        result = prime * result + getAccessibilityViewId(mSourceNodeId);
1594        result = prime * result + getVirtualDescendantId(mSourceNodeId);
1595        result = prime * result + mWindowId;
1596        return result;
1597    }
1598
1599    @Override
1600    public String toString() {
1601        StringBuilder builder = new StringBuilder();
1602        builder.append(super.toString());
1603
1604        if (DEBUG) {
1605            builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
1606            builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
1607            builder.append("; mParentNodeId: " + mParentNodeId);
1608
1609            int granularities = mGranularities;
1610            builder.append("; granularities: [");
1611            while (granularities != 0) {
1612                final int granularity = 1 << Integer.numberOfTrailingZeros(granularities);
1613                granularities &= ~granularity;
1614                builder.append(getGranularitySymbolicName(granularity));
1615                if (granularities != 0) {
1616                    builder.append(", ");
1617                }
1618            }
1619            builder.append("]");
1620
1621            SparseLongArray childIds = mChildNodeIds;
1622            builder.append("; childAccessibilityIds: [");
1623            for (int i = 0, count = childIds.size(); i < count; i++) {
1624                builder.append(childIds.valueAt(i));
1625                if (i < count - 1) {
1626                    builder.append(", ");
1627                }
1628            }
1629            builder.append("]");
1630        }
1631
1632        builder.append("; boundsInParent: " + mBoundsInParent);
1633        builder.append("; boundsInScreen: " + mBoundsInScreen);
1634
1635        builder.append("; packageName: ").append(mPackageName);
1636        builder.append("; className: ").append(mClassName);
1637        builder.append("; text: ").append(mText);
1638        builder.append("; contentDescription: ").append(mContentDescription);
1639
1640        builder.append("; checkable: ").append(isCheckable());
1641        builder.append("; checked: ").append(isChecked());
1642        builder.append("; focusable: ").append(isFocusable());
1643        builder.append("; focused: ").append(isFocused());
1644        builder.append("; selected: ").append(isSelected());
1645        builder.append("; clickable: ").append(isClickable());
1646        builder.append("; longClickable: ").append(isLongClickable());
1647        builder.append("; enabled: ").append(isEnabled());
1648        builder.append("; password: ").append(isPassword());
1649        builder.append("; scrollable: " + isScrollable());
1650
1651        builder.append("; [");
1652        for (int actionBits = mActions; actionBits != 0;) {
1653            final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
1654            actionBits &= ~action;
1655            builder.append(getActionSymbolicName(action));
1656            if (actionBits != 0) {
1657                builder.append(", ");
1658            }
1659        }
1660        builder.append("]");
1661
1662        return builder.toString();
1663    }
1664
1665    /**
1666     * @see Parcelable.Creator
1667     */
1668    public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
1669            new Parcelable.Creator<AccessibilityNodeInfo>() {
1670        public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
1671            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1672            info.initFromParcel(parcel);
1673            return info;
1674        }
1675
1676        public AccessibilityNodeInfo[] newArray(int size) {
1677            return new AccessibilityNodeInfo[size];
1678        }
1679    };
1680}
1681