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