• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.TestApi;
22 import android.app.ActivityTaskManager;
23 import android.graphics.Rect;
24 import android.graphics.Region;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.text.TextUtils;
28 import android.util.LongArray;
29 import android.util.Pools.SynchronizedPool;
30 import android.util.SparseArray;
31 import android.view.Display;
32 import android.view.accessibility.AccessibilityEvent.WindowsChangeTypes;
33 
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.Objects;
37 import java.util.concurrent.atomic.AtomicInteger;
38 
39 /**
40  * This class represents a state snapshot of a window for accessibility
41  * purposes. The screen content contains one or more windows where some
42  * windows can be descendants of other windows, which is the windows are
43  * hierarchically ordered. Note that there is no root window. Hence, the
44  * screen content can be seen as a collection of window trees.
45  */
46 public final class AccessibilityWindowInfo implements Parcelable {
47 
48     private static final boolean DEBUG = false;
49 
50     /**
51      * Window type: This is an application window. Such a window shows UI for
52      * interacting with an application.
53      */
54     public static final int TYPE_APPLICATION = 1;
55 
56     /**
57      * Window type: This is an input method window. Such a window shows UI for
58      * inputting text such as keyboard, suggestions, etc.
59      */
60     public static final int TYPE_INPUT_METHOD = 2;
61 
62     /**
63      * Window type: This is a system window. Such a window shows UI for
64      * interacting with the system.
65      */
66     public static final int TYPE_SYSTEM = 3;
67 
68     /**
69      * Window type: Windows that are overlaid <em>only</em> by an {@link
70      * android.accessibilityservice.AccessibilityService} for interception of
71      * user interactions without changing the windows an accessibility service
72      * can introspect. In particular, an accessibility service can introspect
73      * only windows that a sighted user can interact with which they can touch
74      * these windows or can type into these windows. For example, if there
75      * is a full screen accessibility overlay that is touchable, the windows
76      * below it will be introspectable by an accessibility service regardless
77      * they are covered by a touchable window.
78      */
79     public static final int TYPE_ACCESSIBILITY_OVERLAY = 4;
80 
81     /**
82      * Window type: A system window used to divide the screen in split-screen mode.
83      * This type of window is present only in split-screen mode.
84      */
85     public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5;
86 
87     /**
88      * Window type: A system window used to show the UI for the interaction with
89      * window-based magnification, which includes the magnified content and the option menu.
90      */
91     public static final int TYPE_MAGNIFICATION_OVERLAY = 6;
92 
93     /* Special values for window IDs */
94     /** @hide */
95     public static final int ACTIVE_WINDOW_ID = Integer.MAX_VALUE;
96     /** @hide */
97     public static final int UNDEFINED_CONNECTION_ID = -1;
98     /** @hide */
99     public static final int UNDEFINED_WINDOW_ID = -1;
100     /** @hide */
101     public static final int ANY_WINDOW_ID = -2;
102     /** @hide */
103     public static final int PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID = -3;
104 
105     private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0;
106     private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 1;
107     private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 1 << 2;
108     private static final int BOOLEAN_PROPERTY_PICTURE_IN_PICTURE = 1 << 3;
109 
110     // Housekeeping.
111     private static final int MAX_POOL_SIZE = 10;
112     private static final SynchronizedPool<AccessibilityWindowInfo> sPool =
113             new SynchronizedPool<AccessibilityWindowInfo>(MAX_POOL_SIZE);
114     // TODO(b/129300068): Remove sNumInstancesInUse.
115     private static AtomicInteger sNumInstancesInUse;
116 
117     // Data.
118     private int mDisplayId = Display.INVALID_DISPLAY;
119     private int mType = UNDEFINED_WINDOW_ID;
120     private int mLayer = UNDEFINED_WINDOW_ID;
121     private int mBooleanProperties;
122     private int mId = UNDEFINED_WINDOW_ID;
123     private int mParentId = UNDEFINED_WINDOW_ID;
124     private int mTaskId = ActivityTaskManager.INVALID_TASK_ID;
125     private Region mRegionInScreen = new Region();
126     private LongArray mChildIds;
127     private CharSequence mTitle;
128     private long mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
129 
130     private int mConnectionId = UNDEFINED_CONNECTION_ID;
131 
132     /**
133      * Creates a new {@link AccessibilityWindowInfo}.
134      */
AccessibilityWindowInfo()135     public AccessibilityWindowInfo() {
136     }
137 
138     /**
139      * Copy constructor. Creates a new {@link AccessibilityWindowInfo}, and this new instance is
140      * initialized from given <code>info</code>.
141      *
142      * @param info The other info.
143      */
AccessibilityWindowInfo(@onNull AccessibilityWindowInfo info)144     public AccessibilityWindowInfo(@NonNull AccessibilityWindowInfo info) {
145         init(info);
146     }
147 
148     /**
149      * Gets the title of the window.
150      *
151      * @return The title of the window, or {@code null} if none is available.
152      */
153     @Nullable
getTitle()154     public CharSequence getTitle() {
155         return mTitle;
156     }
157 
158     /**
159      * Sets the title of the window.
160      *
161      * @param title The title.
162      *
163      * @hide
164      */
setTitle(CharSequence title)165     public void setTitle(CharSequence title) {
166         mTitle = title;
167     }
168 
169     /**
170      * Gets the type of the window.
171      *
172      * @return The type.
173      *
174      * @see #TYPE_APPLICATION
175      * @see #TYPE_INPUT_METHOD
176      * @see #TYPE_SYSTEM
177      * @see #TYPE_ACCESSIBILITY_OVERLAY
178      */
getType()179     public int getType() {
180         return mType;
181     }
182 
183     /**
184      * Sets the type of the window.
185      *
186      * @param type The type
187      *
188      * @hide
189      */
setType(int type)190     public void setType(int type) {
191         mType = type;
192     }
193 
194     /**
195      * Gets the layer which determines the Z-order of the window. Windows
196      * with greater layer appear on top of windows with lesser layer.
197      *
198      * @return The window layer.
199      */
getLayer()200     public int getLayer() {
201         return mLayer;
202     }
203 
204     /**
205      * Sets the layer which determines the Z-order of the window. Windows
206      * with greater layer appear on top of windows with lesser layer.
207      *
208      * @param layer The window layer.
209      *
210      * @hide
211      */
setLayer(int layer)212     public void setLayer(int layer) {
213         mLayer = layer;
214     }
215 
216     /**
217      * Gets the root node in the window's hierarchy.
218      *
219      * @return The root node.
220      */
getRoot()221     public AccessibilityNodeInfo getRoot() {
222         return getRoot(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID);
223     }
224 
225     /**
226      * Gets the root node in the window's hierarchy.
227      *
228      * @param prefetchingStrategy the prefetching strategy.
229      * @return The root node.
230      *
231      * @see AccessibilityNodeInfo#getParent(int) for a description of prefetching.
232      */
233     @Nullable
getRoot( @ccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy)234     public AccessibilityNodeInfo getRoot(
235             @AccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy) {
236         if (mConnectionId == UNDEFINED_WINDOW_ID) {
237             return null;
238         }
239         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
240         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
241                 mId, AccessibilityNodeInfo.ROOT_NODE_ID,
242                 true, prefetchingStrategy, null);
243     }
244 
245     /**
246      * Sets the anchor node's ID.
247      *
248      * @param anchorId The anchor's accessibility id in its window.
249      *
250      * @hide
251      */
setAnchorId(long anchorId)252     public void setAnchorId(long anchorId) {
253         mAnchorId = anchorId;
254     }
255 
256     /**
257      * Gets the node that anchors this window to another.
258      *
259      * @return The anchor node, or {@code null} if none exists.
260      */
getAnchor()261     public AccessibilityNodeInfo getAnchor() {
262         if ((mConnectionId == UNDEFINED_WINDOW_ID)
263                 || (mAnchorId == AccessibilityNodeInfo.UNDEFINED_NODE_ID)
264                 || (mParentId == UNDEFINED_WINDOW_ID)) {
265             return null;
266         }
267 
268         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
269         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
270                 mParentId, mAnchorId, true, 0, null);
271     }
272 
273     /** @hide */
setPictureInPicture(boolean pictureInPicture)274     public void setPictureInPicture(boolean pictureInPicture) {
275         setBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE, pictureInPicture);
276     }
277 
278     /**
279      * Check if the window is in picture-in-picture mode.
280      *
281      * @return {@code true} if the window is in picture-in-picture mode, {@code false} otherwise.
282      */
isInPictureInPictureMode()283     public boolean isInPictureInPictureMode() {
284         return getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE);
285     }
286 
287     /**
288      * Gets the parent window.
289      *
290      * @return The parent window, or {@code null} if none exists.
291      */
getParent()292     public AccessibilityWindowInfo getParent() {
293         if (mConnectionId == UNDEFINED_WINDOW_ID || mParentId == UNDEFINED_WINDOW_ID) {
294             return null;
295         }
296         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
297         return client.getWindow(mConnectionId, mParentId);
298     }
299 
300     /**
301      * Sets the parent window id.
302      *
303      * @param parentId The parent id.
304      *
305      * @hide
306      */
setParentId(int parentId)307     public void setParentId(int parentId) {
308         mParentId = parentId;
309     }
310 
311     /**
312      * Gets the unique window id.
313      *
314      * @return windowId The window id.
315      */
getId()316     public int getId() {
317         return mId;
318     }
319 
320     /**
321      * Sets the unique window id.
322      *
323      * @param id The window id.
324      *
325      * @hide
326      */
setId(int id)327     public void setId(int id) {
328         mId = id;
329     }
330 
331     /**
332      * Gets the task ID.
333      *
334      * @return The task ID.
335      *
336      * @hide
337      */
getTaskId()338     public int getTaskId() {
339         return mTaskId;
340     }
341 
342     /**
343      * Sets the task ID.
344      *
345      * @param taskId The task ID.
346      *
347      * @hide
348      */
setTaskId(int taskId)349     public void setTaskId(int taskId) {
350         mTaskId = taskId;
351     }
352 
353     /**
354      * Sets the unique id of the IAccessibilityServiceConnection over which
355      * this instance can send requests to the system.
356      *
357      * @param connectionId The connection id.
358      *
359      * @hide
360      */
setConnectionId(int connectionId)361     public void setConnectionId(int connectionId) {
362         mConnectionId = connectionId;
363     }
364 
365     /**
366      * Gets the touchable region of this window in the screen.
367      *
368      * @param outRegion The out window region.
369      */
getRegionInScreen(@onNull Region outRegion)370     public void getRegionInScreen(@NonNull Region outRegion) {
371         outRegion.set(mRegionInScreen);
372     }
373 
374     /**
375      * Sets the touchable region of this window in the screen.
376      *
377      * @param region The window region.
378      *
379      * @hide
380      */
setRegionInScreen(Region region)381     public void setRegionInScreen(Region region) {
382         mRegionInScreen.set(region);
383     }
384 
385     /**
386      * Gets the bounds of this window in the screen. This is equivalent to get the bounds of the
387      * Region from {@link #getRegionInScreen(Region)}.
388      *
389      * @param outBounds The out window bounds.
390      */
getBoundsInScreen(Rect outBounds)391     public void getBoundsInScreen(Rect outBounds) {
392         outBounds.set(mRegionInScreen.getBounds());
393     }
394 
395     /**
396      * Gets if this window is active. An active window is the one
397      * the user is currently touching or the window has input focus
398      * and the user is not touching any window.
399      * <p>
400      * This is defined as the window that most recently fired one
401      * of the following events:
402      * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
403      * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
404      * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}.
405      * In other words, the last window shown that also has input focus.
406      * </p>
407      *
408      * @return Whether this is the active window.
409      */
isActive()410     public boolean isActive() {
411         return getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE);
412     }
413 
414     /**
415      * Sets if this window is active, which is this is the window
416      * the user is currently touching or the window has input focus
417      * and the user is not touching any window.
418      *
419      * @param active Whether this is the active window.
420      *
421      * @hide
422      */
setActive(boolean active)423     public void setActive(boolean active) {
424         setBooleanProperty(BOOLEAN_PROPERTY_ACTIVE, active);
425     }
426 
427     /**
428      * Gets if this window has input focus.
429      *
430      * @return Whether has input focus.
431      */
isFocused()432     public boolean isFocused() {
433         return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED);
434     }
435 
436     /**
437      * Sets if this window has input focus.
438      *
439      * @param focused Whether has input focus.
440      *
441      * @hide
442      */
setFocused(boolean focused)443     public void setFocused(boolean focused) {
444         setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
445     }
446 
447     /**
448      * Gets if this window has accessibility focus.
449      *
450      * @return Whether has accessibility focus.
451      */
isAccessibilityFocused()452     public boolean isAccessibilityFocused() {
453         return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED);
454     }
455 
456     /**
457      * Sets if this window has accessibility focus.
458      *
459      * @param focused Whether has accessibility focus.
460      *
461      * @hide
462      */
setAccessibilityFocused(boolean focused)463     public void setAccessibilityFocused(boolean focused) {
464         setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused);
465     }
466 
467     /**
468      * Gets the number of child windows.
469      *
470      * @return The child count.
471      */
getChildCount()472     public int getChildCount() {
473         return (mChildIds != null) ? mChildIds.size() : 0;
474     }
475 
476     /**
477      * Gets the child window at a given index.
478      *
479      * @param index The index.
480      * @return The child.
481      */
getChild(int index)482     public AccessibilityWindowInfo getChild(int index) {
483         if (mChildIds == null) {
484             throw new IndexOutOfBoundsException();
485         }
486         if (mConnectionId == UNDEFINED_WINDOW_ID) {
487             return null;
488         }
489         final int childId = (int) mChildIds.get(index);
490         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
491         return client.getWindow(mConnectionId, childId);
492     }
493 
494     /**
495      * Adds a child window.
496      *
497      * @param childId The child window id.
498      *
499      * @hide
500      */
addChild(int childId)501     public void addChild(int childId) {
502         if (mChildIds == null) {
503             mChildIds = new LongArray();
504         }
505         mChildIds.add(childId);
506     }
507 
508     /**
509      * Sets the display Id.
510      *
511      * @param displayId The display id.
512      *
513      * @hide
514      */
setDisplayId(int displayId)515     public void setDisplayId(int displayId) {
516         mDisplayId = displayId;
517     }
518 
519     /**
520      * Returns the ID of the display this window is on, for use with
521      * {@link android.hardware.display.DisplayManager#getDisplay(int)}.
522      *
523      * @return The logical display id.
524      */
getDisplayId()525     public int getDisplayId() {
526         return mDisplayId;
527     }
528 
529     /**
530      * Returns a cached instance if such is available or a new one is
531      * created.
532      *
533      * <p>In most situations object pooling is not beneficial. Create a new instance using the
534      * constructor {@link #AccessibilityWindowInfo()} instead.
535      *
536      * @return An instance.
537      */
obtain()538     public static AccessibilityWindowInfo obtain() {
539         AccessibilityWindowInfo info = sPool.acquire();
540         if (info == null) {
541             info = new AccessibilityWindowInfo();
542         }
543         if (sNumInstancesInUse != null) {
544             sNumInstancesInUse.incrementAndGet();
545         }
546         return info;
547     }
548 
549     /**
550      * Returns a cached instance if such is available or a new one is
551      * created. The returned instance is initialized from the given
552      * <code>info</code>.
553      *
554      * <p>In most situations object pooling is not beneficial. Create a new instance using the
555      * constructor {@link #AccessibilityWindowInfo(AccessibilityWindowInfo)} instead.
556      *
557      * @param info The other info.
558      * @return An instance.
559      */
obtain(AccessibilityWindowInfo info)560     public static AccessibilityWindowInfo obtain(AccessibilityWindowInfo info) {
561         AccessibilityWindowInfo infoClone = obtain();
562         infoClone.init(info);
563         return infoClone;
564     }
565 
566     /**
567      * Specify a counter that will be incremented on obtain() and decremented on recycle()
568      *
569      * @hide
570      */
571     @TestApi
setNumInstancesInUseCounter(AtomicInteger counter)572     public static void setNumInstancesInUseCounter(AtomicInteger counter) {
573         if (sNumInstancesInUse != null) {
574             sNumInstancesInUse = counter;
575         }
576     }
577 
578     /**
579      * Return an instance back to be reused.
580      * <p>
581      * <strong>Note:</strong> You must not touch the object after calling this function.
582      * </p>
583      *
584      * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
585      *
586      * @throws IllegalStateException If the info is already recycled.
587      */
recycle()588     public void recycle() {
589         clear();
590         sPool.release(this);
591         if (sNumInstancesInUse != null) {
592             sNumInstancesInUse.decrementAndGet();
593         }
594     }
595 
596     /**
597      * Refreshes this window with the latest state of the window it represents.
598      * <p>
599      * <strong>Note:</strong> If this method returns false this info is obsolete
600      * since it represents a window that is no longer exist.
601      * </p>
602      *
603      * @hide
604      */
refresh()605     public boolean refresh() {
606         if (mConnectionId == UNDEFINED_CONNECTION_ID || mId == UNDEFINED_WINDOW_ID) {
607             return false;
608         }
609         final AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
610         final AccessibilityWindowInfo refreshedInfo = client.getWindow(mConnectionId,
611                 mId, /* bypassCache */true);
612         if (refreshedInfo == null) {
613             return false;
614         }
615         init(refreshedInfo);
616         refreshedInfo.recycle();
617         return true;
618     }
619 
620     @Override
describeContents()621     public int describeContents() {
622         return 0;
623     }
624 
625     @Override
writeToParcel(Parcel parcel, int flags)626     public void writeToParcel(Parcel parcel, int flags) {
627         parcel.writeInt(mDisplayId);
628         parcel.writeInt(mType);
629         parcel.writeInt(mLayer);
630         parcel.writeInt(mBooleanProperties);
631         parcel.writeInt(mId);
632         parcel.writeInt(mParentId);
633         parcel.writeInt(mTaskId);
634         mRegionInScreen.writeToParcel(parcel, flags);
635         parcel.writeCharSequence(mTitle);
636         parcel.writeLong(mAnchorId);
637 
638         final LongArray childIds = mChildIds;
639         if (childIds == null) {
640             parcel.writeInt(0);
641         } else {
642             final int childCount = childIds.size();
643             parcel.writeInt(childCount);
644             for (int i = 0; i < childCount; i++) {
645                 parcel.writeInt((int) childIds.get(i));
646             }
647         }
648 
649         parcel.writeInt(mConnectionId);
650     }
651 
652     /**
653      * Initializes this instance from another one.
654      *
655      * @param other The other instance.
656      */
init(AccessibilityWindowInfo other)657     private void init(AccessibilityWindowInfo other) {
658         mDisplayId = other.mDisplayId;
659         mType = other.mType;
660         mLayer = other.mLayer;
661         mBooleanProperties = other.mBooleanProperties;
662         mId = other.mId;
663         mParentId = other.mParentId;
664         mTaskId = other.mTaskId;
665         mRegionInScreen.set(other.mRegionInScreen);
666         mTitle = other.mTitle;
667         mAnchorId = other.mAnchorId;
668 
669         if (mChildIds != null) mChildIds.clear();
670         if (other.mChildIds != null && other.mChildIds.size() > 0) {
671             if (mChildIds == null) {
672                 mChildIds = other.mChildIds.clone();
673             } else {
674                 mChildIds.addAll(other.mChildIds);
675             }
676         }
677 
678         mConnectionId = other.mConnectionId;
679     }
680 
initFromParcel(Parcel parcel)681     private void initFromParcel(Parcel parcel) {
682         mDisplayId = parcel.readInt();
683         mType = parcel.readInt();
684         mLayer = parcel.readInt();
685         mBooleanProperties = parcel.readInt();
686         mId = parcel.readInt();
687         mParentId = parcel.readInt();
688         mTaskId = parcel.readInt();
689         mRegionInScreen = Region.CREATOR.createFromParcel(parcel);
690         mTitle = parcel.readCharSequence();
691         mAnchorId = parcel.readLong();
692 
693         final int childCount = parcel.readInt();
694         if (childCount > 0) {
695             if (mChildIds == null) {
696                 mChildIds = new LongArray(childCount);
697             }
698             for (int i = 0; i < childCount; i++) {
699                 final int childId = parcel.readInt();
700                 mChildIds.add(childId);
701             }
702         }
703 
704         mConnectionId = parcel.readInt();
705     }
706 
707     @Override
hashCode()708     public int hashCode() {
709         return mId;
710     }
711 
712     @Override
equals(@ullable Object obj)713     public boolean equals(@Nullable Object obj) {
714         if (this == obj) {
715             return true;
716         }
717         if (obj == null) {
718             return false;
719         }
720         if (getClass() != obj.getClass()) {
721             return false;
722         }
723         AccessibilityWindowInfo other = (AccessibilityWindowInfo) obj;
724         return (mId == other.mId);
725     }
726 
727     @Override
toString()728     public String toString() {
729         StringBuilder builder = new StringBuilder();
730         builder.append("AccessibilityWindowInfo[");
731         builder.append("title=").append(mTitle);
732         builder.append(", displayId=").append(mDisplayId);
733         builder.append(", id=").append(mId);
734         builder.append(", taskId=").append(mTaskId);
735         builder.append(", type=").append(typeToString(mType));
736         builder.append(", layer=").append(mLayer);
737         builder.append(", region=").append(mRegionInScreen);
738         builder.append(", bounds=").append(mRegionInScreen.getBounds());
739         builder.append(", focused=").append(isFocused());
740         builder.append(", active=").append(isActive());
741         builder.append(", pictureInPicture=").append(isInPictureInPictureMode());
742         if (DEBUG) {
743             builder.append(", parent=").append(mParentId);
744             builder.append(", children=[");
745             if (mChildIds != null) {
746                 final int childCount = mChildIds.size();
747                 for (int i = 0; i < childCount; i++) {
748                     builder.append(mChildIds.get(i));
749                     if (i < childCount - 1) {
750                         builder.append(',');
751                     }
752                 }
753             } else {
754                 builder.append("null");
755             }
756             builder.append(']');
757         } else {
758             builder.append(", hasParent=").append(mParentId != UNDEFINED_WINDOW_ID);
759             builder.append(", isAnchored=")
760                     .append(mAnchorId != AccessibilityNodeInfo.UNDEFINED_NODE_ID);
761             builder.append(", hasChildren=").append(mChildIds != null
762                     && mChildIds.size() > 0);
763         }
764         builder.append(']');
765         return builder.toString();
766     }
767 
768     /**
769      * Clears the internal state.
770      */
clear()771     private void clear() {
772         mDisplayId = Display.INVALID_DISPLAY;
773         mType = UNDEFINED_WINDOW_ID;
774         mLayer = UNDEFINED_WINDOW_ID;
775         mBooleanProperties = 0;
776         mId = UNDEFINED_WINDOW_ID;
777         mParentId = UNDEFINED_WINDOW_ID;
778         mTaskId = ActivityTaskManager.INVALID_TASK_ID;
779         mRegionInScreen.setEmpty();
780         mChildIds = null;
781         mConnectionId = UNDEFINED_WINDOW_ID;
782         mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
783         mTitle = null;
784     }
785 
786     /**
787      * Gets the value of a boolean property.
788      *
789      * @param property The property.
790      * @return The value.
791      */
getBooleanProperty(int property)792     private boolean getBooleanProperty(int property) {
793         return (mBooleanProperties & property) != 0;
794     }
795 
796     /**
797      * Sets a boolean property.
798      *
799      * @param property The property.
800      * @param value The value.
801      *
802      * @throws IllegalStateException If called from an AccessibilityService.
803      */
setBooleanProperty(int property, boolean value)804     private void setBooleanProperty(int property, boolean value) {
805         if (value) {
806             mBooleanProperties |= property;
807         } else {
808             mBooleanProperties &= ~property;
809         }
810     }
811 
812     /**
813      * @hide
814      */
typeToString(int type)815     public static String typeToString(int type) {
816         switch (type) {
817             case TYPE_APPLICATION: {
818                 return "TYPE_APPLICATION";
819             }
820             case TYPE_INPUT_METHOD: {
821                 return "TYPE_INPUT_METHOD";
822             }
823             case TYPE_SYSTEM: {
824                 return "TYPE_SYSTEM";
825             }
826             case TYPE_ACCESSIBILITY_OVERLAY: {
827                 return "TYPE_ACCESSIBILITY_OVERLAY";
828             }
829             case TYPE_SPLIT_SCREEN_DIVIDER: {
830                 return "TYPE_SPLIT_SCREEN_DIVIDER";
831             }
832             case TYPE_MAGNIFICATION_OVERLAY: {
833                 return "TYPE_MAGNIFICATION_OVERLAY";
834             }
835             default:
836                 return "<UNKNOWN:" + type + ">";
837         }
838     }
839 
840     /**
841      * Reports how this window differs from a possibly different state of the same window. The
842      * argument must have the same id and type as neither of those properties may change.
843      *
844      * @param other The new state.
845      * @return A set of flags showing how the window has changes, or 0 if the two states are the
846      * same.
847      *
848      * @hide
849      */
850     @WindowsChangeTypes
differenceFrom(AccessibilityWindowInfo other)851     public int differenceFrom(AccessibilityWindowInfo other) {
852         if (other.mId != mId) {
853             throw new IllegalArgumentException("Not same window.");
854         }
855         if (other.mType != mType) {
856             throw new IllegalArgumentException("Not same type.");
857         }
858         int changes = 0;
859         if (!TextUtils.equals(mTitle, other.mTitle)) {
860             changes |= AccessibilityEvent.WINDOWS_CHANGE_TITLE;
861         }
862         if (!mRegionInScreen.equals(other.mRegionInScreen)) {
863             changes |= AccessibilityEvent.WINDOWS_CHANGE_BOUNDS;
864         }
865         if (mLayer != other.mLayer) {
866             changes |= AccessibilityEvent.WINDOWS_CHANGE_LAYER;
867         }
868         if (getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)
869                 != other.getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)) {
870             changes |= AccessibilityEvent.WINDOWS_CHANGE_ACTIVE;
871         }
872         if (getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)
873                 != other.getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)) {
874             changes |= AccessibilityEvent.WINDOWS_CHANGE_FOCUSED;
875         }
876         if (getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)
877                 != other.getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)) {
878             changes |= AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
879         }
880         if (getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)
881                 != other.getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)) {
882             changes |= AccessibilityEvent.WINDOWS_CHANGE_PIP;
883         }
884         if (mParentId != other.mParentId) {
885             changes |= AccessibilityEvent.WINDOWS_CHANGE_PARENT;
886         }
887         if (!Objects.equals(mChildIds, other.mChildIds)) {
888             changes |= AccessibilityEvent.WINDOWS_CHANGE_CHILDREN;
889         }
890         //TODO(b/1338122): Add DISPLAY_CHANGED type for multi-display
891         return changes;
892     }
893 
894     public static final @android.annotation.NonNull Parcelable.Creator<AccessibilityWindowInfo> CREATOR =
895             new Creator<AccessibilityWindowInfo>() {
896         @Override
897         public AccessibilityWindowInfo createFromParcel(Parcel parcel) {
898             AccessibilityWindowInfo info = obtain();
899             info.initFromParcel(parcel);
900             return info;
901         }
902 
903         @Override
904         public AccessibilityWindowInfo[] newArray(int size) {
905             return new AccessibilityWindowInfo[size];
906         }
907     };
908 
909     /**
910      * Transfers a sparsearray with lists having {@link AccessibilityWindowInfo}s across an IPC.
911      * The key of this sparsearray is display Id.
912      *
913      * @hide
914      */
915     public static final class WindowListSparseArray
916             extends SparseArray<List<AccessibilityWindowInfo>> implements Parcelable {
917 
918         @Override
describeContents()919         public int describeContents() {
920             return 0;
921         }
922 
923         @Override
writeToParcel(Parcel dest, int flags)924         public void writeToParcel(Parcel dest, int flags) {
925             final int count = size();
926             dest.writeInt(count);
927             for (int i = 0; i < count; i++) {
928                 dest.writeParcelableList(valueAt(i), 0);
929                 dest.writeInt(keyAt(i));
930             }
931         }
932 
933         public static final Parcelable.Creator<WindowListSparseArray> CREATOR =
934                 new Parcelable.Creator<WindowListSparseArray>() {
935             public WindowListSparseArray createFromParcel(
936                     Parcel source) {
937                 final WindowListSparseArray array = new WindowListSparseArray();
938                 final ClassLoader loader = array.getClass().getClassLoader();
939                 final int count = source.readInt();
940                 for (int i = 0; i < count; i++) {
941                     List<AccessibilityWindowInfo> windows = new ArrayList<>();
942                     source.readParcelableList(windows, loader, android.view.accessibility.AccessibilityWindowInfo.class);
943                     array.put(source.readInt(), windows);
944                 }
945                 return array;
946             }
947 
948             public WindowListSparseArray[] newArray(int size) {
949                 return new WindowListSparseArray[size];
950             }
951         };
952     }
953 }
954