• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view.accessibility;
18 
19 import android.graphics.Rect;
20 import android.os.Bundle;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.util.SparseLongArray;
24 import android.view.View;
25 
26 import java.util.Collections;
27 import java.util.List;
28 
29 /**
30  * This class represents a node of the window content as well as actions that
31  * can be requested from its source. From the point of view of an
32  * {@link android.accessibilityservice.AccessibilityService} a window content is
33  * presented as tree of accessibility node info which may or may not map one-to-one
34  * to the view hierarchy. In other words, a custom view is free to report itself as
35  * a tree of accessibility node info.
36  * </p>
37  * <p>
38  * Once an accessibility node info is delivered to an accessibility service it is
39  * made immutable and calling a state mutation method generates an error.
40  * </p>
41  * <p>
42  * Please refer to {@link android.accessibilityservice.AccessibilityService} for
43  * details about how to obtain a handle to window content as a tree of accessibility
44  * node info as well as familiarizing with the security model.
45  * </p>
46  * <div class="special reference">
47  * <h3>Developer Guides</h3>
48  * <p>For more information about making applications accessible, read the
49  * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
50  * developer guide.</p>
51  * </div>
52  *
53  * @see android.accessibilityservice.AccessibilityService
54  * @see AccessibilityEvent
55  * @see AccessibilityManager
56  */
57 public class AccessibilityNodeInfo implements Parcelable {
58 
59     private static final boolean DEBUG = false;
60 
61     /** @hide */
62     public static final int UNDEFINED = -1;
63 
64     /** @hide */
65     public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED);
66 
67     /** @hide */
68     public static final int ACTIVE_WINDOW_ID = UNDEFINED;
69 
70     /** @hide */
71     public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
72 
73     /** @hide */
74     public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
75 
76     /** @hide */
77     public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004;
78 
79     /** @hide */
80     public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008;
81 
82     // Actions.
83 
84     /**
85      * Action that gives input focus to the node.
86      */
87     public static final int ACTION_FOCUS =  0x00000001;
88 
89     /**
90      * Action that clears input focus of the node.
91      */
92     public static final int ACTION_CLEAR_FOCUS = 0x00000002;
93 
94     /**
95      * Action that selects the node.
96      */
97     public static final int ACTION_SELECT = 0x00000004;
98 
99     /**
100      * Action that unselects the node.
101      */
102     public static final int ACTION_CLEAR_SELECTION = 0x00000008;
103 
104     /**
105      * Action that clicks on the node info.
106      */
107     public static final int ACTION_CLICK = 0x00000010;
108 
109     /**
110      * Action that long clicks on the node.
111      */
112     public static final int ACTION_LONG_CLICK = 0x00000020;
113 
114     /**
115      * Action that gives accessibility focus to the node.
116      */
117     public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;
118 
119     /**
120      * Action that clears accessibility focus of the node.
121      */
122     public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
123 
124     /**
125      * Action that requests to go to the next entity in this node's text
126      * at a given movement granularity. For example, move to the next character,
127      * word, etc.
128      * <p>
129      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br>
130      * <strong>Example:</strong>
131      * <code><pre><p>
132      *   Bundle arguments = new Bundle();
133      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
134      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
135      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
136      * </code></pre></p>
137      * </p>
138      *
139      * @see #setMovementGranularities(int)
140      * @see #getMovementGranularities()
141      *
142      * @see #MOVEMENT_GRANULARITY_CHARACTER
143      * @see #MOVEMENT_GRANULARITY_WORD
144      * @see #MOVEMENT_GRANULARITY_LINE
145      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
146      * @see #MOVEMENT_GRANULARITY_PAGE
147      */
148     public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;
149 
150     /**
151      * Action that requests to go to the previous entity in this node's text
152      * at a given movement granularity. For example, move to the next character,
153      * word, etc.
154      * <p>
155      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br>
156      * <strong>Example:</strong>
157      * <code><pre><p>
158      *   Bundle arguments = new Bundle();
159      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
160      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
161      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
162      *           arguments);
163      * </code></pre></p>
164      * </p>
165      *
166      * @see #setMovementGranularities(int)
167      * @see #getMovementGranularities()
168      *
169      * @see #MOVEMENT_GRANULARITY_CHARACTER
170      * @see #MOVEMENT_GRANULARITY_WORD
171      * @see #MOVEMENT_GRANULARITY_LINE
172      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
173      * @see #MOVEMENT_GRANULARITY_PAGE
174      */
175     public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;
176 
177     /**
178      * Action to move to the next HTML element of a given type. For example, move
179      * to the BUTTON, INPUT, TABLE, etc.
180      * <p>
181      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
182      * <strong>Example:</strong>
183      * <code><pre><p>
184      *   Bundle arguments = new Bundle();
185      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
186      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
187      * </code></pre></p>
188      * </p>
189      */
190     public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
191 
192     /**
193      * Action to move to the previous HTML element of a given type. For example, move
194      * to the BUTTON, INPUT, TABLE, etc.
195      * <p>
196      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
197      * <strong>Example:</strong>
198      * <code><pre><p>
199      *   Bundle arguments = new Bundle();
200      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
201      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
202      * </code></pre></p>
203      * </p>
204      */
205     public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
206 
207     /**
208      * Action to scroll the node content forward.
209      */
210     public static final int ACTION_SCROLL_FORWARD = 0x00001000;
211 
212     /**
213      * Action to scroll the node content backward.
214      */
215     public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
216 
217     /**
218      * Argument for which movement granularity to be used when traversing the node text.
219      * <p>
220      * <strong>Type:</strong> int<br>
221      * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
222      * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
223      * </p>
224      */
225     public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
226         "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
227 
228     /**
229      * Argument for which HTML element to get moving to the next/previous HTML element.
230      * <p>
231      * <strong>Type:</strong> String<br>
232      * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
233      *         {@link #ACTION_PREVIOUS_HTML_ELEMENT}
234      * </p>
235      */
236     public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
237         "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
238 
239     /**
240      * The input focus.
241      */
242     public static final int FOCUS_INPUT = 1;
243 
244     /**
245      * The accessibility focus.
246      */
247     public static final int FOCUS_ACCESSIBILITY = 2;
248 
249     // Movement granularities
250 
251     /**
252      * Movement granularity bit for traversing the text of a node by character.
253      */
254     public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001;
255 
256     /**
257      * Movement granularity bit for traversing the text of a node by word.
258      */
259     public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002;
260 
261     /**
262      * Movement granularity bit for traversing the text of a node by line.
263      */
264     public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004;
265 
266     /**
267      * Movement granularity bit for traversing the text of a node by paragraph.
268      */
269     public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008;
270 
271     /**
272      * Movement granularity bit for traversing the text of a node by page.
273      */
274     public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;
275 
276     // Boolean attributes.
277 
278     private static final int PROPERTY_CHECKABLE = 0x00000001;
279 
280     private static final int PROPERTY_CHECKED = 0x00000002;
281 
282     private static final int PROPERTY_FOCUSABLE = 0x00000004;
283 
284     private static final int PROPERTY_FOCUSED = 0x00000008;
285 
286     private static final int PROPERTY_SELECTED = 0x00000010;
287 
288     private static final int PROPERTY_CLICKABLE = 0x00000020;
289 
290     private static final int PROPERTY_LONG_CLICKABLE = 0x00000040;
291 
292     private static final int PROPERTY_ENABLED = 0x00000080;
293 
294     private static final int PROPERTY_PASSWORD = 0x00000100;
295 
296     private static final int PROPERTY_SCROLLABLE = 0x00000200;
297 
298     private static final int PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
299 
300     private static final int PROPERTY_VISIBLE_TO_USER = 0x00000800;
301 
302     /**
303      * Bits that provide the id of a virtual descendant of a view.
304      */
305     private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L;
306 
307     /**
308      * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a
309      * virtual descendant of a view. Such a descendant does not exist in the view
310      * hierarchy and is only reported via the accessibility APIs.
311      */
312     private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
313 
314     /**
315      * Gets the accessibility view id which identifies a View in the view three.
316      *
317      * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
318      * @return The accessibility view id part of the node id.
319      *
320      * @hide
321      */
getAccessibilityViewId(long accessibilityNodeId)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      */
getVirtualDescendantId(long accessibilityNodeId)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      */
makeNodeId(int accessibilityViewId, int virtualDescendantId)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     private long mLabelForId = ROOT_NODE_ID;
369     private long mLabeledById = ROOT_NODE_ID;
370 
371     private int mBooleanProperties;
372     private final Rect mBoundsInParent = new Rect();
373     private final Rect mBoundsInScreen = new Rect();
374 
375     private CharSequence mPackageName;
376     private CharSequence mClassName;
377     private CharSequence mText;
378     private CharSequence mContentDescription;
379 
380     private final SparseLongArray mChildNodeIds = new SparseLongArray();
381     private int mActions;
382 
383     private int mMovementGranularities;
384 
385     private int mConnectionId = UNDEFINED;
386 
387     /**
388      * Hide constructor from clients.
389      */
AccessibilityNodeInfo()390     private AccessibilityNodeInfo() {
391         /* do nothing */
392     }
393 
394     /**
395      * Sets the source.
396      * <p>
397      *   <strong>Note:</strong> Cannot be called from an
398      *   {@link android.accessibilityservice.AccessibilityService}.
399      *   This class is made immutable before being delivered to an AccessibilityService.
400      * </p>
401      *
402      * @param source The info source.
403      */
setSource(View source)404     public void setSource(View source) {
405         setSource(source, UNDEFINED);
406     }
407 
408     /**
409      * Sets the source to be a virtual descendant of the given <code>root</code>.
410      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
411      * is set as the source.
412      * <p>
413      * A virtual descendant is an imaginary View that is reported as a part of the view
414      * hierarchy for accessibility purposes. This enables custom views that draw complex
415      * content to report themselves as a tree of virtual views, thus conveying their
416      * logical structure.
417      * </p>
418      * <p>
419      *   <strong>Note:</strong> Cannot be called from an
420      *   {@link android.accessibilityservice.AccessibilityService}.
421      *   This class is made immutable before being delivered to an AccessibilityService.
422      * </p>
423      *
424      * @param root The root of the virtual subtree.
425      * @param virtualDescendantId The id of the virtual descendant.
426      */
setSource(View root, int virtualDescendantId)427     public void setSource(View root, int virtualDescendantId) {
428         enforceNotSealed();
429         mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
430         final int rootAccessibilityViewId =
431             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
432         mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
433     }
434 
435     /**
436      * Find the view that has the specified focus type. The search starts from
437      * the view represented by this node info.
438      *
439      * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
440      *         {@link #FOCUS_ACCESSIBILITY}.
441      * @return The node info of the focused view or null.
442      *
443      * @see #FOCUS_INPUT
444      * @see #FOCUS_ACCESSIBILITY
445      */
findFocus(int focus)446     public AccessibilityNodeInfo findFocus(int focus) {
447         enforceSealed();
448         enforceValidFocusType(focus);
449         if (!canPerformRequestOverConnection(mSourceNodeId)) {
450             return null;
451         }
452         return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId,
453                 mSourceNodeId, focus);
454     }
455 
456     /**
457      * Searches for the nearest view in the specified direction that can take
458      * the input focus.
459      *
460      * @param direction The direction. Can be one of:
461      *     {@link View#FOCUS_DOWN},
462      *     {@link View#FOCUS_UP},
463      *     {@link View#FOCUS_LEFT},
464      *     {@link View#FOCUS_RIGHT},
465      *     {@link View#FOCUS_FORWARD},
466      *     {@link View#FOCUS_BACKWARD}.
467      *
468      * @return The node info for the view that can take accessibility focus.
469      */
focusSearch(int direction)470     public AccessibilityNodeInfo focusSearch(int direction) {
471         enforceSealed();
472         enforceValidFocusDirection(direction);
473         if (!canPerformRequestOverConnection(mSourceNodeId)) {
474             return null;
475         }
476         return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId,
477                 mSourceNodeId, direction);
478     }
479 
480     /**
481      * Gets the id of the window from which the info comes from.
482      *
483      * @return The window id.
484      */
getWindowId()485     public int getWindowId() {
486         return mWindowId;
487     }
488 
489     /**
490      * @return The ids of the children.
491      *
492      * @hide
493      */
getChildNodeIds()494     public SparseLongArray getChildNodeIds() {
495         return mChildNodeIds;
496     }
497 
498     /**
499      * Gets the number of children.
500      *
501      * @return The child count.
502      */
getChildCount()503     public int getChildCount() {
504         return mChildNodeIds.size();
505     }
506 
507     /**
508      * Get the child at given index.
509      * <p>
510      *   <strong>Note:</strong> It is a client responsibility to recycle the
511      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
512      *     to avoid creating of multiple instances.
513      * </p>
514      *
515      * @param index The child index.
516      * @return The child node.
517      *
518      * @throws IllegalStateException If called outside of an AccessibilityService.
519      *
520      */
getChild(int index)521     public AccessibilityNodeInfo getChild(int index) {
522         enforceSealed();
523         if (!canPerformRequestOverConnection(mSourceNodeId)) {
524             return null;
525         }
526         final long childId = mChildNodeIds.get(index);
527         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
528         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
529                 childId, FLAG_PREFETCH_DESCENDANTS);
530     }
531 
532     /**
533      * Adds a child.
534      * <p>
535      * <strong>Note:</strong> Cannot be called from an
536      * {@link android.accessibilityservice.AccessibilityService}.
537      * This class is made immutable before being delivered to an AccessibilityService.
538      * </p>
539      *
540      * @param child The child.
541      *
542      * @throws IllegalStateException If called from an AccessibilityService.
543      */
addChild(View child)544     public void addChild(View child) {
545         addChild(child, UNDEFINED);
546     }
547 
548     /**
549      * Adds a virtual child which is a descendant of the given <code>root</code>.
550      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
551      * is added as a child.
552      * <p>
553      * A virtual descendant is an imaginary View that is reported as a part of the view
554      * hierarchy for accessibility purposes. This enables custom views that draw complex
555      * content to report them selves as a tree of virtual views, thus conveying their
556      * logical structure.
557      * </p>
558      *
559      * @param root The root of the virtual subtree.
560      * @param virtualDescendantId The id of the virtual child.
561      */
addChild(View root, int virtualDescendantId)562     public void addChild(View root, int virtualDescendantId) {
563         enforceNotSealed();
564         final int index = mChildNodeIds.size();
565         final int rootAccessibilityViewId =
566             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
567         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
568         mChildNodeIds.put(index, childNodeId);
569     }
570 
571     /**
572      * Gets the actions that can be performed on the node.
573      *
574      * @return The bit mask of with actions.
575      *
576      * @see AccessibilityNodeInfo#ACTION_FOCUS
577      * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
578      * @see AccessibilityNodeInfo#ACTION_SELECT
579      * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
580      * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS
581      * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS
582      * @see AccessibilityNodeInfo#ACTION_CLICK
583      * @see AccessibilityNodeInfo#ACTION_LONG_CLICK
584      * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
585      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
586      * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT
587      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT
588      * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD
589      * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD
590      */
getActions()591     public int getActions() {
592         return mActions;
593     }
594 
595     /**
596      * Adds an action that can be performed on the node.
597      * <p>
598      *   <strong>Note:</strong> Cannot be called from an
599      *   {@link android.accessibilityservice.AccessibilityService}.
600      *   This class is made immutable before being delivered to an AccessibilityService.
601      * </p>
602      *
603      * @param action The action.
604      *
605      * @throws IllegalStateException If called from an AccessibilityService.
606      */
addAction(int action)607     public void addAction(int action) {
608         enforceNotSealed();
609         mActions |= action;
610     }
611 
612     /**
613      * Sets the movement granularities for traversing the text of this node.
614      * <p>
615      *   <strong>Note:</strong> Cannot be called from an
616      *   {@link android.accessibilityservice.AccessibilityService}.
617      *   This class is made immutable before being delivered to an AccessibilityService.
618      * </p>
619      *
620      * @param granularities The bit mask with granularities.
621      *
622      * @throws IllegalStateException If called from an AccessibilityService.
623      */
setMovementGranularities(int granularities)624     public void setMovementGranularities(int granularities) {
625         enforceNotSealed();
626         mMovementGranularities = granularities;
627     }
628 
629     /**
630      * Gets the movement granularities for traversing the text of this node.
631      *
632      * @return The bit mask with granularities.
633      */
getMovementGranularities()634     public int getMovementGranularities() {
635         return mMovementGranularities;
636     }
637 
638     /**
639      * Performs an action on the node.
640      * <p>
641      *   <strong>Note:</strong> An action can be performed only if the request is made
642      *   from an {@link android.accessibilityservice.AccessibilityService}.
643      * </p>
644      *
645      * @param action The action to perform.
646      * @return True if the action was performed.
647      *
648      * @throws IllegalStateException If called outside of an AccessibilityService.
649      */
performAction(int action)650     public boolean performAction(int action) {
651         enforceSealed();
652         if (!canPerformRequestOverConnection(mSourceNodeId)) {
653             return false;
654         }
655         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
656         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
657                 action, null);
658     }
659 
660     /**
661      * Performs an action on the node.
662      * <p>
663      *   <strong>Note:</strong> An action can be performed only if the request is made
664      *   from an {@link android.accessibilityservice.AccessibilityService}.
665      * </p>
666      *
667      * @param action The action to perform.
668      * @param arguments A bundle with additional arguments.
669      * @return True if the action was performed.
670      *
671      * @throws IllegalStateException If called outside of an AccessibilityService.
672      */
performAction(int action, Bundle arguments)673     public boolean performAction(int action, Bundle arguments) {
674         enforceSealed();
675         if (!canPerformRequestOverConnection(mSourceNodeId)) {
676             return false;
677         }
678         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
679         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
680                 action, arguments);
681     }
682 
683     /**
684      * Finds {@link AccessibilityNodeInfo}s by text. The match is case
685      * insensitive containment. The search is relative to this info i.e.
686      * this info is the root of the traversed tree.
687      *
688      * <p>
689      *   <strong>Note:</strong> It is a client responsibility to recycle the
690      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
691      *     to avoid creating of multiple instances.
692      * </p>
693      *
694      * @param text The searched text.
695      * @return A list of node info.
696      */
findAccessibilityNodeInfosByText(String text)697     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
698         enforceSealed();
699         if (!canPerformRequestOverConnection(mSourceNodeId)) {
700             return Collections.emptyList();
701         }
702         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
703         return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
704                 text);
705     }
706 
707     /**
708      * Gets the parent.
709      * <p>
710      *   <strong>Note:</strong> It is a client responsibility to recycle the
711      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
712      *     to avoid creating of multiple instances.
713      * </p>
714      *
715      * @return The parent.
716      */
getParent()717     public AccessibilityNodeInfo getParent() {
718         enforceSealed();
719         if (!canPerformRequestOverConnection(mParentNodeId)) {
720             return null;
721         }
722         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
723         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
724                 mWindowId, mParentNodeId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
725     }
726 
727     /**
728      * @return The parent node id.
729      *
730      * @hide
731      */
getParentNodeId()732     public long getParentNodeId() {
733         return mParentNodeId;
734     }
735 
736     /**
737      * Sets the parent.
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 parent The parent.
745      *
746      * @throws IllegalStateException If called from an AccessibilityService.
747      */
setParent(View parent)748     public void setParent(View parent) {
749         setParent(parent, UNDEFINED);
750     }
751 
752     /**
753      * Sets the parent to be a virtual descendant of the given <code>root</code>.
754      * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
755      * is set as the parent.
756      * <p>
757      * A virtual descendant is an imaginary View that is reported as a part of the view
758      * hierarchy for accessibility purposes. This enables custom views that draw complex
759      * content to report them selves as a tree of virtual views, thus conveying their
760      * logical structure.
761      * </p>
762      * <p>
763      *   <strong>Note:</strong> Cannot be called from an
764      *   {@link android.accessibilityservice.AccessibilityService}.
765      *   This class is made immutable before being delivered to an AccessibilityService.
766      * </p>
767      *
768      * @param root The root of the virtual subtree.
769      * @param virtualDescendantId The id of the virtual descendant.
770      */
setParent(View root, int virtualDescendantId)771     public void setParent(View root, int virtualDescendantId) {
772         enforceNotSealed();
773         final int rootAccessibilityViewId =
774             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
775         mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
776     }
777 
778     /**
779      * Gets the node bounds in parent coordinates.
780      *
781      * @param outBounds The output node bounds.
782      */
getBoundsInParent(Rect outBounds)783     public void getBoundsInParent(Rect outBounds) {
784         outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
785                 mBoundsInParent.right, mBoundsInParent.bottom);
786     }
787 
788     /**
789      * Sets the node bounds in parent coordinates.
790      * <p>
791      *   <strong>Note:</strong> Cannot be called from an
792      *   {@link android.accessibilityservice.AccessibilityService}.
793      *   This class is made immutable before being delivered to an AccessibilityService.
794      * </p>
795      *
796      * @param bounds The node bounds.
797      *
798      * @throws IllegalStateException If called from an AccessibilityService.
799      */
setBoundsInParent(Rect bounds)800     public void setBoundsInParent(Rect bounds) {
801         enforceNotSealed();
802         mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
803     }
804 
805     /**
806      * Gets the node bounds in screen coordinates.
807      *
808      * @param outBounds The output node bounds.
809      */
getBoundsInScreen(Rect outBounds)810     public void getBoundsInScreen(Rect outBounds) {
811         outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top,
812                 mBoundsInScreen.right, mBoundsInScreen.bottom);
813     }
814 
815     /**
816      * Sets the node bounds in screen coordinates.
817      * <p>
818      *   <strong>Note:</strong> Cannot be called from an
819      *   {@link android.accessibilityservice.AccessibilityService}.
820      *   This class is made immutable before being delivered to an AccessibilityService.
821      * </p>
822      *
823      * @param bounds The node bounds.
824      *
825      * @throws IllegalStateException If called from an AccessibilityService.
826      */
setBoundsInScreen(Rect bounds)827     public void setBoundsInScreen(Rect bounds) {
828         enforceNotSealed();
829         mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
830     }
831 
832     /**
833      * Gets whether this node is checkable.
834      *
835      * @return True if the node is checkable.
836      */
isCheckable()837     public boolean isCheckable() {
838         return getBooleanProperty(PROPERTY_CHECKABLE);
839     }
840 
841     /**
842      * Sets whether this node is checkable.
843      * <p>
844      *   <strong>Note:</strong> Cannot be called from an
845      *   {@link android.accessibilityservice.AccessibilityService}.
846      *   This class is made immutable before being delivered to an AccessibilityService.
847      * </p>
848      *
849      * @param checkable True if the node is checkable.
850      *
851      * @throws IllegalStateException If called from an AccessibilityService.
852      */
setCheckable(boolean checkable)853     public void setCheckable(boolean checkable) {
854         setBooleanProperty(PROPERTY_CHECKABLE, checkable);
855     }
856 
857     /**
858      * Gets whether this node is checked.
859      *
860      * @return True if the node is checked.
861      */
isChecked()862     public boolean isChecked() {
863         return getBooleanProperty(PROPERTY_CHECKED);
864     }
865 
866     /**
867      * Sets whether this node is checked.
868      * <p>
869      *   <strong>Note:</strong> Cannot be called from an
870      *   {@link android.accessibilityservice.AccessibilityService}.
871      *   This class is made immutable before being delivered to an AccessibilityService.
872      * </p>
873      *
874      * @param checked True if the node is checked.
875      *
876      * @throws IllegalStateException If called from an AccessibilityService.
877      */
setChecked(boolean checked)878     public void setChecked(boolean checked) {
879         setBooleanProperty(PROPERTY_CHECKED, checked);
880     }
881 
882     /**
883      * Gets whether this node is focusable.
884      *
885      * @return True if the node is focusable.
886      */
isFocusable()887     public boolean isFocusable() {
888         return getBooleanProperty(PROPERTY_FOCUSABLE);
889     }
890 
891     /**
892      * Sets whether this node is focusable.
893      * <p>
894      *   <strong>Note:</strong> Cannot be called from an
895      *   {@link android.accessibilityservice.AccessibilityService}.
896      *   This class is made immutable before being delivered to an AccessibilityService.
897      * </p>
898      *
899      * @param focusable True if the node is focusable.
900      *
901      * @throws IllegalStateException If called from an AccessibilityService.
902      */
setFocusable(boolean focusable)903     public void setFocusable(boolean focusable) {
904         setBooleanProperty(PROPERTY_FOCUSABLE, focusable);
905     }
906 
907     /**
908      * Gets whether this node is focused.
909      *
910      * @return True if the node is focused.
911      */
isFocused()912     public boolean isFocused() {
913         return getBooleanProperty(PROPERTY_FOCUSED);
914     }
915 
916     /**
917      * Sets whether this node is focused.
918      * <p>
919      *   <strong>Note:</strong> Cannot be called from an
920      *   {@link android.accessibilityservice.AccessibilityService}.
921      *   This class is made immutable before being delivered to an AccessibilityService.
922      * </p>
923      *
924      * @param focused True if the node is focused.
925      *
926      * @throws IllegalStateException If called from an AccessibilityService.
927      */
setFocused(boolean focused)928     public void setFocused(boolean focused) {
929         setBooleanProperty(PROPERTY_FOCUSED, focused);
930     }
931 
932     /**
933      * Sets whether this node is visible to the user.
934      *
935      * @return Whether the node is visible to the user.
936      */
isVisibleToUser()937     public boolean isVisibleToUser() {
938         return getBooleanProperty(PROPERTY_VISIBLE_TO_USER);
939     }
940 
941     /**
942      * Sets whether this node is visible to the user.
943      * <p>
944      *   <strong>Note:</strong> Cannot be called from an
945      *   {@link android.accessibilityservice.AccessibilityService}.
946      *   This class is made immutable before being delivered to an AccessibilityService.
947      * </p>
948      *
949      * @param visibleToUser Whether the node is visible to the user.
950      *
951      * @throws IllegalStateException If called from an AccessibilityService.
952      */
setVisibleToUser(boolean visibleToUser)953     public void setVisibleToUser(boolean visibleToUser) {
954         setBooleanProperty(PROPERTY_VISIBLE_TO_USER, visibleToUser);
955     }
956 
957     /**
958      * Gets whether this node is accessibility focused.
959      *
960      * @return True if the node is accessibility focused.
961      */
isAccessibilityFocused()962     public boolean isAccessibilityFocused() {
963         return getBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED);
964     }
965 
966     /**
967      * Sets whether this node is accessibility focused.
968      * <p>
969      *   <strong>Note:</strong> Cannot be called from an
970      *   {@link android.accessibilityservice.AccessibilityService}.
971      *   This class is made immutable before being delivered to an AccessibilityService.
972      * </p>
973      *
974      * @param focused True if the node is accessibility focused.
975      *
976      * @throws IllegalStateException If called from an AccessibilityService.
977      */
setAccessibilityFocused(boolean focused)978     public void setAccessibilityFocused(boolean focused) {
979         setBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED, focused);
980     }
981 
982     /**
983      * Gets whether this node is selected.
984      *
985      * @return True if the node is selected.
986      */
isSelected()987     public boolean isSelected() {
988         return getBooleanProperty(PROPERTY_SELECTED);
989     }
990 
991     /**
992      * Sets whether this node is selected.
993      * <p>
994      *   <strong>Note:</strong> Cannot be called from an
995      *   {@link android.accessibilityservice.AccessibilityService}.
996      *   This class is made immutable before being delivered to an AccessibilityService.
997      * </p>
998      *
999      * @param selected True if the node is selected.
1000      *
1001      * @throws IllegalStateException If called from an AccessibilityService.
1002      */
setSelected(boolean selected)1003     public void setSelected(boolean selected) {
1004         setBooleanProperty(PROPERTY_SELECTED, selected);
1005     }
1006 
1007     /**
1008      * Gets whether this node is clickable.
1009      *
1010      * @return True if the node is clickable.
1011      */
isClickable()1012     public boolean isClickable() {
1013         return getBooleanProperty(PROPERTY_CLICKABLE);
1014     }
1015 
1016     /**
1017      * Sets whether this node is clickable.
1018      * <p>
1019      *   <strong>Note:</strong> Cannot be called from an
1020      *   {@link android.accessibilityservice.AccessibilityService}.
1021      *   This class is made immutable before being delivered to an AccessibilityService.
1022      * </p>
1023      *
1024      * @param clickable True if the node is clickable.
1025      *
1026      * @throws IllegalStateException If called from an AccessibilityService.
1027      */
setClickable(boolean clickable)1028     public void setClickable(boolean clickable) {
1029         setBooleanProperty(PROPERTY_CLICKABLE, clickable);
1030     }
1031 
1032     /**
1033      * Gets whether this node is long clickable.
1034      *
1035      * @return True if the node is long clickable.
1036      */
isLongClickable()1037     public boolean isLongClickable() {
1038         return getBooleanProperty(PROPERTY_LONG_CLICKABLE);
1039     }
1040 
1041     /**
1042      * Sets whether this node is long clickable.
1043      * <p>
1044      *   <strong>Note:</strong> Cannot be called from an
1045      *   {@link android.accessibilityservice.AccessibilityService}.
1046      *   This class is made immutable before being delivered to an AccessibilityService.
1047      * </p>
1048      *
1049      * @param longClickable True if the node is long clickable.
1050      *
1051      * @throws IllegalStateException If called from an AccessibilityService.
1052      */
setLongClickable(boolean longClickable)1053     public void setLongClickable(boolean longClickable) {
1054         setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable);
1055     }
1056 
1057     /**
1058      * Gets whether this node is enabled.
1059      *
1060      * @return True if the node is enabled.
1061      */
isEnabled()1062     public boolean isEnabled() {
1063         return getBooleanProperty(PROPERTY_ENABLED);
1064     }
1065 
1066     /**
1067      * Sets whether this node is enabled.
1068      * <p>
1069      *   <strong>Note:</strong> Cannot be called from an
1070      *   {@link android.accessibilityservice.AccessibilityService}.
1071      *   This class is made immutable before being delivered to an AccessibilityService.
1072      * </p>
1073      *
1074      * @param enabled True if the node is enabled.
1075      *
1076      * @throws IllegalStateException If called from an AccessibilityService.
1077      */
setEnabled(boolean enabled)1078     public void setEnabled(boolean enabled) {
1079         setBooleanProperty(PROPERTY_ENABLED, enabled);
1080     }
1081 
1082     /**
1083      * Gets whether this node is a password.
1084      *
1085      * @return True if the node is a password.
1086      */
isPassword()1087     public boolean isPassword() {
1088         return getBooleanProperty(PROPERTY_PASSWORD);
1089     }
1090 
1091     /**
1092      * Sets whether this node is a password.
1093      * <p>
1094      *   <strong>Note:</strong> Cannot be called from an
1095      *   {@link android.accessibilityservice.AccessibilityService}.
1096      *   This class is made immutable before being delivered to an AccessibilityService.
1097      * </p>
1098      *
1099      * @param password True if the node is a password.
1100      *
1101      * @throws IllegalStateException If called from an AccessibilityService.
1102      */
setPassword(boolean password)1103     public void setPassword(boolean password) {
1104         setBooleanProperty(PROPERTY_PASSWORD, password);
1105     }
1106 
1107     /**
1108      * Gets if the node is scrollable.
1109      *
1110      * @return True if the node is scrollable, false otherwise.
1111      */
isScrollable()1112     public boolean isScrollable() {
1113         return getBooleanProperty(PROPERTY_SCROLLABLE);
1114     }
1115 
1116     /**
1117      * Sets if the node is scrollable.
1118      * <p>
1119      *   <strong>Note:</strong> Cannot be called from an
1120      *   {@link android.accessibilityservice.AccessibilityService}.
1121      *   This class is made immutable before being delivered to an AccessibilityService.
1122      * </p>
1123      *
1124      * @param scrollable True if the node is scrollable, false otherwise.
1125      *
1126      * @throws IllegalStateException If called from an AccessibilityService.
1127      */
setScrollable(boolean scrollable)1128     public void setScrollable(boolean scrollable) {
1129         enforceNotSealed();
1130         setBooleanProperty(PROPERTY_SCROLLABLE, scrollable);
1131     }
1132 
1133     /**
1134      * Gets the package this node comes from.
1135      *
1136      * @return The package name.
1137      */
getPackageName()1138     public CharSequence getPackageName() {
1139         return mPackageName;
1140     }
1141 
1142     /**
1143      * Sets the package this node comes from.
1144      * <p>
1145      *   <strong>Note:</strong> Cannot be called from an
1146      *   {@link android.accessibilityservice.AccessibilityService}.
1147      *   This class is made immutable before being delivered to an AccessibilityService.
1148      * </p>
1149      *
1150      * @param packageName The package name.
1151      *
1152      * @throws IllegalStateException If called from an AccessibilityService.
1153      */
setPackageName(CharSequence packageName)1154     public void setPackageName(CharSequence packageName) {
1155         enforceNotSealed();
1156         mPackageName = packageName;
1157     }
1158 
1159     /**
1160      * Gets the class this node comes from.
1161      *
1162      * @return The class name.
1163      */
getClassName()1164     public CharSequence getClassName() {
1165         return mClassName;
1166     }
1167 
1168     /**
1169      * Sets the class this node comes from.
1170      * <p>
1171      *   <strong>Note:</strong> Cannot be called from an
1172      *   {@link android.accessibilityservice.AccessibilityService}.
1173      *   This class is made immutable before being delivered to an AccessibilityService.
1174      * </p>
1175      *
1176      * @param className The class name.
1177      *
1178      * @throws IllegalStateException If called from an AccessibilityService.
1179      */
setClassName(CharSequence className)1180     public void setClassName(CharSequence className) {
1181         enforceNotSealed();
1182         mClassName = className;
1183     }
1184 
1185     /**
1186      * Gets the text of this node.
1187      *
1188      * @return The text.
1189      */
getText()1190     public CharSequence getText() {
1191         return mText;
1192     }
1193 
1194     /**
1195      * Sets the text of this node.
1196      * <p>
1197      *   <strong>Note:</strong> Cannot be called from an
1198      *   {@link android.accessibilityservice.AccessibilityService}.
1199      *   This class is made immutable before being delivered to an AccessibilityService.
1200      * </p>
1201      *
1202      * @param text The text.
1203      *
1204      * @throws IllegalStateException If called from an AccessibilityService.
1205      */
setText(CharSequence text)1206     public void setText(CharSequence text) {
1207         enforceNotSealed();
1208         mText = text;
1209     }
1210 
1211     /**
1212      * Gets the content description of this node.
1213      *
1214      * @return The content description.
1215      */
getContentDescription()1216     public CharSequence getContentDescription() {
1217         return mContentDescription;
1218     }
1219 
1220     /**
1221      * Sets the content description of this node.
1222      * <p>
1223      *   <strong>Note:</strong> Cannot be called from an
1224      *   {@link android.accessibilityservice.AccessibilityService}.
1225      *   This class is made immutable before being delivered to an AccessibilityService.
1226      * </p>
1227      *
1228      * @param contentDescription The content description.
1229      *
1230      * @throws IllegalStateException If called from an AccessibilityService.
1231      */
setContentDescription(CharSequence contentDescription)1232     public void setContentDescription(CharSequence contentDescription) {
1233         enforceNotSealed();
1234         mContentDescription = contentDescription;
1235     }
1236 
1237     /**
1238      * Sets the view for which the view represented by this info serves as a
1239      * label for accessibility purposes.
1240      *
1241      * @param labeled The view for which this info serves as a label.
1242      */
setLabelFor(View labeled)1243     public void setLabelFor(View labeled) {
1244         setLabelFor(labeled, UNDEFINED);
1245     }
1246 
1247     /**
1248      * Sets the view for which the view represented by this info serves as a
1249      * label for accessibility purposes. If <code>virtualDescendantId</code>
1250      * is {@link View#NO_ID} the root is set as the labeled.
1251      * <p>
1252      * A virtual descendant is an imaginary View that is reported as a part of the view
1253      * hierarchy for accessibility purposes. This enables custom views that draw complex
1254      * content to report themselves as a tree of virtual views, thus conveying their
1255      * logical structure.
1256      * </p>
1257      * <p>
1258      *   <strong>Note:</strong> Cannot be called from an
1259      *   {@link android.accessibilityservice.AccessibilityService}.
1260      *   This class is made immutable before being delivered to an AccessibilityService.
1261      * </p>
1262      *
1263      * @param root The root whose virtual descendant serves as a label.
1264      * @param virtualDescendantId The id of the virtual descendant.
1265      */
setLabelFor(View root, int virtualDescendantId)1266     public void setLabelFor(View root, int virtualDescendantId) {
1267         enforceNotSealed();
1268         final int rootAccessibilityViewId = (root != null)
1269                 ? root.getAccessibilityViewId() : UNDEFINED;
1270         mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
1271     }
1272 
1273     /**
1274      * Gets the node info for which the view represented by this info serves as
1275      * a label for accessibility purposes.
1276      * <p>
1277      *   <strong>Note:</strong> It is a client responsibility to recycle the
1278      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
1279      *     to avoid creating of multiple instances.
1280      * </p>
1281      *
1282      * @return The labeled info.
1283      */
getLabelFor()1284     public AccessibilityNodeInfo getLabelFor() {
1285         enforceSealed();
1286         if (!canPerformRequestOverConnection(mLabelForId)) {
1287             return null;
1288         }
1289         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1290         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
1291                 mWindowId, mLabelForId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
1292     }
1293 
1294     /**
1295      * Sets the view which serves as the label of the view represented by
1296      * this info for accessibility purposes.
1297      *
1298      * @param label The view that labels this node's source.
1299      */
setLabeledBy(View label)1300     public void setLabeledBy(View label) {
1301         setLabeledBy(label, UNDEFINED);
1302     }
1303 
1304     /**
1305      * Sets the view which serves as the label of the view represented by
1306      * this info for accessibility purposes. If <code>virtualDescendantId</code>
1307      * is {@link View#NO_ID} the root is set as the label.
1308      * <p>
1309      * A virtual descendant is an imaginary View that is reported as a part of the view
1310      * hierarchy for accessibility purposes. This enables custom views that draw complex
1311      * content to report themselves as a tree of virtual views, thus conveying their
1312      * logical structure.
1313      * </p>
1314      * <p>
1315      *   <strong>Note:</strong> Cannot be called from an
1316      *   {@link android.accessibilityservice.AccessibilityService}.
1317      *   This class is made immutable before being delivered to an AccessibilityService.
1318      * </p>
1319      *
1320      * @param root The root whose virtual descendant labels this node's source.
1321      * @param virtualDescendantId The id of the virtual descendant.
1322      */
setLabeledBy(View root, int virtualDescendantId)1323     public void setLabeledBy(View root, int virtualDescendantId) {
1324         enforceNotSealed();
1325         final int rootAccessibilityViewId = (root != null)
1326                 ? root.getAccessibilityViewId() : UNDEFINED;
1327         mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
1328     }
1329 
1330     /**
1331      * Gets the node info which serves as the label of the view represented by
1332      * this info for accessibility purposes.
1333      * <p>
1334      *   <strong>Note:</strong> It is a client responsibility to recycle the
1335      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
1336      *     to avoid creating of multiple instances.
1337      * </p>
1338      *
1339      * @return The label.
1340      */
getLabeledBy()1341     public AccessibilityNodeInfo getLabeledBy() {
1342         enforceSealed();
1343         if (!canPerformRequestOverConnection(mLabeledById)) {
1344             return null;
1345         }
1346         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1347         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
1348                 mWindowId, mLabeledById, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
1349     }
1350 
1351     /**
1352      * Gets the value of a boolean property.
1353      *
1354      * @param property The property.
1355      * @return The value.
1356      */
getBooleanProperty(int property)1357     private boolean getBooleanProperty(int property) {
1358         return (mBooleanProperties & property) != 0;
1359     }
1360 
1361     /**
1362      * Sets a boolean property.
1363      *
1364      * @param property The property.
1365      * @param value The value.
1366      *
1367      * @throws IllegalStateException If called from an AccessibilityService.
1368      */
setBooleanProperty(int property, boolean value)1369     private void setBooleanProperty(int property, boolean value) {
1370         enforceNotSealed();
1371         if (value) {
1372             mBooleanProperties |= property;
1373         } else {
1374             mBooleanProperties &= ~property;
1375         }
1376     }
1377 
1378     /**
1379      * Sets the unique id of the IAccessibilityServiceConnection over which
1380      * this instance can send requests to the system.
1381      *
1382      * @param connectionId The connection id.
1383      *
1384      * @hide
1385      */
setConnectionId(int connectionId)1386     public void setConnectionId(int connectionId) {
1387         enforceNotSealed();
1388         mConnectionId = connectionId;
1389     }
1390 
1391     /**
1392      * {@inheritDoc}
1393      */
describeContents()1394     public int describeContents() {
1395         return 0;
1396     }
1397 
1398     /**
1399      * Gets the id of the source node.
1400      *
1401      * @return The id.
1402      *
1403      * @hide
1404      */
getSourceNodeId()1405     public long getSourceNodeId() {
1406         return mSourceNodeId;
1407     }
1408 
1409     /**
1410      * Sets if this instance is sealed.
1411      *
1412      * @param sealed Whether is sealed.
1413      *
1414      * @hide
1415      */
setSealed(boolean sealed)1416     public void setSealed(boolean sealed) {
1417         mSealed = sealed;
1418     }
1419 
1420     /**
1421      * Gets if this instance is sealed.
1422      *
1423      * @return Whether is sealed.
1424      *
1425      * @hide
1426      */
isSealed()1427     public boolean isSealed() {
1428         return mSealed;
1429     }
1430 
1431     /**
1432      * Enforces that this instance is sealed.
1433      *
1434      * @throws IllegalStateException If this instance is not sealed.
1435      *
1436      * @hide
1437      */
enforceSealed()1438     protected void enforceSealed() {
1439         if (!isSealed()) {
1440             throw new IllegalStateException("Cannot perform this "
1441                     + "action on a not sealed instance.");
1442         }
1443     }
1444 
enforceValidFocusDirection(int direction)1445     private void enforceValidFocusDirection(int direction) {
1446         switch (direction) {
1447             case View.FOCUS_DOWN:
1448             case View.FOCUS_UP:
1449             case View.FOCUS_LEFT:
1450             case View.FOCUS_RIGHT:
1451             case View.FOCUS_FORWARD:
1452             case View.FOCUS_BACKWARD:
1453                 return;
1454             default:
1455                 throw new IllegalArgumentException("Unknown direction: " + direction);
1456         }
1457     }
1458 
enforceValidFocusType(int focusType)1459     private void enforceValidFocusType(int focusType) {
1460         switch (focusType) {
1461             case FOCUS_INPUT:
1462             case FOCUS_ACCESSIBILITY:
1463                 return;
1464             default:
1465                 throw new IllegalArgumentException("Unknown focus type: " + focusType);
1466         }
1467     }
1468 
1469     /**
1470      * Enforces that this instance is not sealed.
1471      *
1472      * @throws IllegalStateException If this instance is sealed.
1473      *
1474      * @hide
1475      */
enforceNotSealed()1476     protected void enforceNotSealed() {
1477         if (isSealed()) {
1478             throw new IllegalStateException("Cannot perform this "
1479                     + "action on a sealed instance.");
1480         }
1481     }
1482 
1483     /**
1484      * Returns a cached instance if such is available otherwise a new one
1485      * and sets the source.
1486      *
1487      * @param source The source view.
1488      * @return An instance.
1489      *
1490      * @see #setSource(View)
1491      */
obtain(View source)1492     public static AccessibilityNodeInfo obtain(View source) {
1493         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1494         info.setSource(source);
1495         return info;
1496     }
1497 
1498     /**
1499      * Returns a cached instance if such is available otherwise a new one
1500      * and sets the source.
1501      *
1502      * @param root The root of the virtual subtree.
1503      * @param virtualDescendantId The id of the virtual descendant.
1504      * @return An instance.
1505      *
1506      * @see #setSource(View, int)
1507      */
obtain(View root, int virtualDescendantId)1508     public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
1509         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1510         info.setSource(root, virtualDescendantId);
1511         return info;
1512     }
1513 
1514     /**
1515      * Returns a cached instance if such is available otherwise a new one.
1516      *
1517      * @return An instance.
1518      */
obtain()1519     public static AccessibilityNodeInfo obtain() {
1520         synchronized (sPoolLock) {
1521             if (sPool != null) {
1522                 AccessibilityNodeInfo info = sPool;
1523                 sPool = sPool.mNext;
1524                 sPoolSize--;
1525                 info.mNext = null;
1526                 info.mIsInPool = false;
1527                 return info;
1528             }
1529             return new AccessibilityNodeInfo();
1530         }
1531     }
1532 
1533     /**
1534      * Returns a cached instance if such is available or a new one is
1535      * create. The returned instance is initialized from the given
1536      * <code>info</code>.
1537      *
1538      * @param info The other info.
1539      * @return An instance.
1540      */
obtain(AccessibilityNodeInfo info)1541     public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
1542         AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
1543         infoClone.init(info);
1544         return infoClone;
1545     }
1546 
1547     /**
1548      * Return an instance back to be reused.
1549      * <p>
1550      * <strong>Note:</strong> You must not touch the object after calling this function.
1551      *
1552      * @throws IllegalStateException If the info is already recycled.
1553      */
recycle()1554     public void recycle() {
1555         if (mIsInPool) {
1556             throw new IllegalStateException("Info already recycled!");
1557         }
1558         clear();
1559         synchronized (sPoolLock) {
1560             if (sPoolSize <= MAX_POOL_SIZE) {
1561                 mNext = sPool;
1562                 sPool = this;
1563                 mIsInPool = true;
1564                 sPoolSize++;
1565             }
1566         }
1567     }
1568 
1569     /**
1570      * {@inheritDoc}
1571      * <p>
1572      *   <strong>Note:</strong> After the instance is written to a parcel it
1573      *      is recycled. You must not touch the object after calling this function.
1574      * </p>
1575      */
writeToParcel(Parcel parcel, int flags)1576     public void writeToParcel(Parcel parcel, int flags) {
1577         parcel.writeInt(isSealed() ? 1 : 0);
1578         parcel.writeLong(mSourceNodeId);
1579         parcel.writeInt(mWindowId);
1580         parcel.writeLong(mParentNodeId);
1581         parcel.writeLong(mLabelForId);
1582         parcel.writeLong(mLabeledById);
1583         parcel.writeInt(mConnectionId);
1584 
1585         SparseLongArray childIds = mChildNodeIds;
1586         final int childIdsSize = childIds.size();
1587         parcel.writeInt(childIdsSize);
1588         for (int i = 0; i < childIdsSize; i++) {
1589             parcel.writeLong(childIds.valueAt(i));
1590         }
1591 
1592         parcel.writeInt(mBoundsInParent.top);
1593         parcel.writeInt(mBoundsInParent.bottom);
1594         parcel.writeInt(mBoundsInParent.left);
1595         parcel.writeInt(mBoundsInParent.right);
1596 
1597         parcel.writeInt(mBoundsInScreen.top);
1598         parcel.writeInt(mBoundsInScreen.bottom);
1599         parcel.writeInt(mBoundsInScreen.left);
1600         parcel.writeInt(mBoundsInScreen.right);
1601 
1602         parcel.writeInt(mActions);
1603 
1604         parcel.writeInt(mMovementGranularities);
1605 
1606         parcel.writeInt(mBooleanProperties);
1607 
1608         parcel.writeCharSequence(mPackageName);
1609         parcel.writeCharSequence(mClassName);
1610         parcel.writeCharSequence(mText);
1611         parcel.writeCharSequence(mContentDescription);
1612 
1613         // Since instances of this class are fetched via synchronous i.e. blocking
1614         // calls in IPCs we always recycle as soon as the instance is marshaled.
1615         recycle();
1616     }
1617 
1618     /**
1619      * Initializes this instance from another one.
1620      *
1621      * @param other The other instance.
1622      */
1623     @SuppressWarnings("unchecked")
init(AccessibilityNodeInfo other)1624     private void init(AccessibilityNodeInfo other) {
1625         mSealed = other.mSealed;
1626         mSourceNodeId = other.mSourceNodeId;
1627         mParentNodeId = other.mParentNodeId;
1628         mLabelForId = other.mLabelForId;
1629         mLabeledById = other.mLabeledById;
1630         mWindowId = other.mWindowId;
1631         mConnectionId = other.mConnectionId;
1632         mBoundsInParent.set(other.mBoundsInParent);
1633         mBoundsInScreen.set(other.mBoundsInScreen);
1634         mPackageName = other.mPackageName;
1635         mClassName = other.mClassName;
1636         mText = other.mText;
1637         mContentDescription = other.mContentDescription;
1638         mActions= other.mActions;
1639         mBooleanProperties = other.mBooleanProperties;
1640         mMovementGranularities = other.mMovementGranularities;
1641         final int otherChildIdCount = other.mChildNodeIds.size();
1642         for (int i = 0; i < otherChildIdCount; i++) {
1643             mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));
1644         }
1645     }
1646 
1647     /**
1648      * Creates a new instance from a {@link Parcel}.
1649      *
1650      * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
1651      */
initFromParcel(Parcel parcel)1652     private void initFromParcel(Parcel parcel) {
1653         mSealed = (parcel.readInt()  == 1);
1654         mSourceNodeId = parcel.readLong();
1655         mWindowId = parcel.readInt();
1656         mParentNodeId = parcel.readLong();
1657         mLabelForId = parcel.readLong();
1658         mLabeledById = parcel.readLong();
1659         mConnectionId = parcel.readInt();
1660 
1661         SparseLongArray childIds = mChildNodeIds;
1662         final int childrenSize = parcel.readInt();
1663         for (int i = 0; i < childrenSize; i++) {
1664             final long childId = parcel.readLong();
1665             childIds.put(i, childId);
1666         }
1667 
1668         mBoundsInParent.top = parcel.readInt();
1669         mBoundsInParent.bottom = parcel.readInt();
1670         mBoundsInParent.left = parcel.readInt();
1671         mBoundsInParent.right = parcel.readInt();
1672 
1673         mBoundsInScreen.top = parcel.readInt();
1674         mBoundsInScreen.bottom = parcel.readInt();
1675         mBoundsInScreen.left = parcel.readInt();
1676         mBoundsInScreen.right = parcel.readInt();
1677 
1678         mActions = parcel.readInt();
1679 
1680         mMovementGranularities = parcel.readInt();
1681 
1682         mBooleanProperties = parcel.readInt();
1683 
1684         mPackageName = parcel.readCharSequence();
1685         mClassName = parcel.readCharSequence();
1686         mText = parcel.readCharSequence();
1687         mContentDescription = parcel.readCharSequence();
1688     }
1689 
1690     /**
1691      * Clears the state of this instance.
1692      */
clear()1693     private void clear() {
1694         mSealed = false;
1695         mSourceNodeId = ROOT_NODE_ID;
1696         mParentNodeId = ROOT_NODE_ID;
1697         mLabelForId = ROOT_NODE_ID;
1698         mLabeledById = ROOT_NODE_ID;
1699         mWindowId = UNDEFINED;
1700         mConnectionId = UNDEFINED;
1701         mMovementGranularities = 0;
1702         mChildNodeIds.clear();
1703         mBoundsInParent.set(0, 0, 0, 0);
1704         mBoundsInScreen.set(0, 0, 0, 0);
1705         mBooleanProperties = 0;
1706         mPackageName = null;
1707         mClassName = null;
1708         mText = null;
1709         mContentDescription = null;
1710         mActions = 0;
1711     }
1712 
1713     /**
1714      * Gets the human readable action symbolic name.
1715      *
1716      * @param action The action.
1717      * @return The symbolic name.
1718      */
getActionSymbolicName(int action)1719     private static String getActionSymbolicName(int action) {
1720         switch (action) {
1721             case ACTION_FOCUS:
1722                 return "ACTION_FOCUS";
1723             case ACTION_CLEAR_FOCUS:
1724                 return "ACTION_CLEAR_FOCUS";
1725             case ACTION_SELECT:
1726                 return "ACTION_SELECT";
1727             case ACTION_CLEAR_SELECTION:
1728                 return "ACTION_CLEAR_SELECTION";
1729             case ACTION_CLICK:
1730                 return "ACTION_CLICK";
1731             case ACTION_LONG_CLICK:
1732                 return "ACTION_LONG_CLICK";
1733             case ACTION_ACCESSIBILITY_FOCUS:
1734                 return "ACTION_ACCESSIBILITY_FOCUS";
1735             case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
1736                 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
1737             case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
1738                 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
1739             case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
1740                 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
1741             case ACTION_NEXT_HTML_ELEMENT:
1742                 return "ACTION_NEXT_HTML_ELEMENT";
1743             case ACTION_PREVIOUS_HTML_ELEMENT:
1744                 return "ACTION_PREVIOUS_HTML_ELEMENT";
1745             case ACTION_SCROLL_FORWARD:
1746                 return "ACTION_SCROLL_FORWARD";
1747             case ACTION_SCROLL_BACKWARD:
1748                 return "ACTION_SCROLL_BACKWARD";
1749             default:
1750                 throw new IllegalArgumentException("Unknown action: " + action);
1751         }
1752     }
1753 
1754     /**
1755      * Gets the human readable movement granularity symbolic name.
1756      *
1757      * @param granularity The granularity.
1758      * @return The symbolic name.
1759      */
getMovementGranularitySymbolicName(int granularity)1760     private static String getMovementGranularitySymbolicName(int granularity) {
1761         switch (granularity) {
1762             case MOVEMENT_GRANULARITY_CHARACTER:
1763                 return "MOVEMENT_GRANULARITY_CHARACTER";
1764             case MOVEMENT_GRANULARITY_WORD:
1765                 return "MOVEMENT_GRANULARITY_WORD";
1766             case MOVEMENT_GRANULARITY_LINE:
1767                 return "MOVEMENT_GRANULARITY_LINE";
1768             case MOVEMENT_GRANULARITY_PARAGRAPH:
1769                 return "MOVEMENT_GRANULARITY_PARAGRAPH";
1770             case MOVEMENT_GRANULARITY_PAGE:
1771                 return "MOVEMENT_GRANULARITY_PAGE";
1772             default:
1773                 throw new IllegalArgumentException("Unknown movement granularity: " + granularity);
1774         }
1775     }
1776 
canPerformRequestOverConnection(long accessibilityNodeId)1777     private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
1778         return (mWindowId != UNDEFINED
1779                 && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
1780                 && mConnectionId != UNDEFINED);
1781     }
1782 
1783     @Override
equals(Object object)1784     public boolean equals(Object object) {
1785         if (this == object) {
1786             return true;
1787         }
1788         if (object == null) {
1789             return false;
1790         }
1791         if (getClass() != object.getClass()) {
1792             return false;
1793         }
1794         AccessibilityNodeInfo other = (AccessibilityNodeInfo) object;
1795         if (mSourceNodeId != other.mSourceNodeId) {
1796             return false;
1797         }
1798         if (mWindowId != other.mWindowId) {
1799             return false;
1800         }
1801         return true;
1802     }
1803 
1804     @Override
hashCode()1805     public int hashCode() {
1806         final int prime = 31;
1807         int result = 1;
1808         result = prime * result + getAccessibilityViewId(mSourceNodeId);
1809         result = prime * result + getVirtualDescendantId(mSourceNodeId);
1810         result = prime * result + mWindowId;
1811         return result;
1812     }
1813 
1814     @Override
toString()1815     public String toString() {
1816         StringBuilder builder = new StringBuilder();
1817         builder.append(super.toString());
1818 
1819         if (DEBUG) {
1820             builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
1821             builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
1822             builder.append("; mParentNodeId: " + mParentNodeId);
1823 
1824             int granularities = mMovementGranularities;
1825             builder.append("; MovementGranularities: [");
1826             while (granularities != 0) {
1827                 final int granularity = 1 << Integer.numberOfTrailingZeros(granularities);
1828                 granularities &= ~granularity;
1829                 builder.append(getMovementGranularitySymbolicName(granularity));
1830                 if (granularities != 0) {
1831                     builder.append(", ");
1832                 }
1833             }
1834             builder.append("]");
1835 
1836             SparseLongArray childIds = mChildNodeIds;
1837             builder.append("; childAccessibilityIds: [");
1838             for (int i = 0, count = childIds.size(); i < count; i++) {
1839                 builder.append(childIds.valueAt(i));
1840                 if (i < count - 1) {
1841                     builder.append(", ");
1842                 }
1843             }
1844             builder.append("]");
1845         }
1846 
1847         builder.append("; boundsInParent: " + mBoundsInParent);
1848         builder.append("; boundsInScreen: " + mBoundsInScreen);
1849 
1850         builder.append("; packageName: ").append(mPackageName);
1851         builder.append("; className: ").append(mClassName);
1852         builder.append("; text: ").append(mText);
1853         builder.append("; contentDescription: ").append(mContentDescription);
1854 
1855         builder.append("; checkable: ").append(isCheckable());
1856         builder.append("; checked: ").append(isChecked());
1857         builder.append("; focusable: ").append(isFocusable());
1858         builder.append("; focused: ").append(isFocused());
1859         builder.append("; selected: ").append(isSelected());
1860         builder.append("; clickable: ").append(isClickable());
1861         builder.append("; longClickable: ").append(isLongClickable());
1862         builder.append("; enabled: ").append(isEnabled());
1863         builder.append("; password: ").append(isPassword());
1864         builder.append("; scrollable: " + isScrollable());
1865 
1866         builder.append("; [");
1867         for (int actionBits = mActions; actionBits != 0;) {
1868             final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
1869             actionBits &= ~action;
1870             builder.append(getActionSymbolicName(action));
1871             if (actionBits != 0) {
1872                 builder.append(", ");
1873             }
1874         }
1875         builder.append("]");
1876 
1877         return builder.toString();
1878     }
1879 
1880     /**
1881      * @see Parcelable.Creator
1882      */
1883     public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
1884             new Parcelable.Creator<AccessibilityNodeInfo>() {
1885         public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
1886             AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1887             info.initFromParcel(parcel);
1888             return info;
1889         }
1890 
1891         public AccessibilityNodeInfo[] newArray(int size) {
1892             return new AccessibilityNodeInfo[size];
1893         }
1894     };
1895 }
1896