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