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