2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.wm;
19 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
20 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
21 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
26 import android.animation.ObjectAnimator;
27 import android.animation.ValueAnimator;
28 import android.annotation.NonNull;
29 import android.app.Service;
30 import android.content.Context;
31 import android.graphics.Canvas;
32 import android.graphics.Color;
33 import android.graphics.Matrix;
34 import android.graphics.Paint;
35 import android.graphics.Path;
36 import android.graphics.PixelFormat;
37 import android.graphics.Point;
38 import android.graphics.PorterDuff.Mode;
39 import android.graphics.Rect;
40 import android.graphics.RectF;
41 import android.graphics.Region;
42 import android.os.Handler;
43 import android.os.IBinder;
44 import android.os.Looper;
45 import android.os.Message;
46 import android.text.TextUtils;
47 import android.util.ArraySet;
48 import android.util.Log;
49 import android.util.Slog;
50 import android.util.SparseArray;
51 import android.util.TypedValue;
52 import android.view.Display;
53 import android.view.MagnificationSpec;
54 import android.view.Surface;
55 import android.view.Surface.OutOfResourcesException;
56 import android.view.SurfaceControl;
57 import android.view.ViewConfiguration;
58 import android.view.WindowInfo;
59 import android.view.WindowManager;
60 import android.view.animation.DecelerateInterpolator;
61 import android.view.animation.Interpolator;
63 import com.android.internal.R;
64 import com.android.internal.os.SomeArgs;
65 import com.android.server.policy.WindowManagerPolicy;
66 import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
67 import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
69 import java.util.ArrayList;
70 import java.util.HashSet;
71 import java.util.List;
75 * This class contains the accessibility related logic of the window manager.
77 final class AccessibilityController {
79 private final WindowManagerService mService;
81 private static final float[] sTempFloats = new float[9];
83 public AccessibilityController(WindowManagerService service) {
87 private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
89 private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
91 public boolean setMagnificationCallbacksLocked(int displayId,
92 MagnificationCallbacks callbacks) {
93 boolean result = false;
94 if (callbacks != null) {
95 if (mDisplayMagnifiers.get(displayId) != null) {
96 throw new IllegalStateException("Magnification callbacks already set!");
98 final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
100 final Display display = dc.getDisplay();
101 if (display != null && display.getType() != Display.TYPE_OVERLAY) {
102 mDisplayMagnifiers.put(displayId, new DisplayMagnifier(
103 mService, dc, display, callbacks));
108 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
109 if (displayMagnifier == null) {
110 throw new IllegalStateException("Magnification callbacks already cleared!");
112 displayMagnifier.destroyLocked();
113 mDisplayMagnifiers.remove(displayId);
119 public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
120 if (callback != null) {
121 if (mWindowsForAccessibilityObserver != null) {
122 throw new IllegalStateException(
123 "Windows for accessibility callback already set!");
125 mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
128 if (mWindowsForAccessibilityObserver == null) {
129 throw new IllegalStateException(
130 "Windows for accessibility callback already cleared!");
132 mWindowsForAccessibilityObserver = null;
136 public void performComputeChangedWindowsNotLocked(boolean forceSend) {
137 WindowsForAccessibilityObserver observer = null;
138 synchronized (mService) {
139 observer = mWindowsForAccessibilityObserver;
141 if (observer != null) {
142 observer.performComputeChangedWindowsNotLocked(forceSend);
146 public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) {
147 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
148 if (displayMagnifier != null) {
149 displayMagnifier.setMagnificationSpecLocked(spec);
151 // TODO: support multi-display for windows observer
152 if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
153 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
157 public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) {
158 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
159 if (displayMagnifier != null) {
160 displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
164 public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) {
165 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
166 if (displayMagnifier != null) {
167 displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
169 // Not relevant for the window observer.
172 public void onWindowLayersChangedLocked(int displayId) {
173 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
174 if (displayMagnifier != null) {
175 displayMagnifier.onWindowLayersChangedLocked();
177 // TODO: support multi-display for windows observer
178 if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
179 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
183 public void onRotationChangedLocked(DisplayContent displayContent) {
184 final int displayId = displayContent.getDisplayId();
185 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
186 if (displayMagnifier != null) {
187 displayMagnifier.onRotationChangedLocked(displayContent);
189 // TODO: support multi-display for windows observer
190 if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
191 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
195 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
196 final int displayId = windowState.getDisplayId();
197 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
198 if (displayMagnifier != null) {
199 displayMagnifier.onAppWindowTransitionLocked(windowState, transition);
201 // Not relevant for the window observer.
204 public void onWindowTransitionLocked(WindowState windowState, int transition) {
205 final int displayId = windowState.getDisplayId();
206 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
207 if (displayMagnifier != null) {
208 displayMagnifier.onWindowTransitionLocked(windowState, transition);
210 // TODO: support multi-display for windows observer
211 if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
212 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
216 public void onWindowFocusChangedNotLocked() {
217 // Not relevant for the display magnifier.
219 WindowsForAccessibilityObserver observer = null;
220 synchronized (mService) {
221 observer = mWindowsForAccessibilityObserver;
223 if (observer != null) {
224 observer.performComputeChangedWindowsNotLocked(false);
228 public void onSomeWindowResizedOrMovedLocked() {
229 // Not relevant for the display magnifier.
231 if (mWindowsForAccessibilityObserver != null) {
232 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
236 /** NOTE: This has to be called within a surface transaction. */
237 public void drawMagnifiedRegionBorderIfNeededLocked(int displayId) {
238 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
239 if (displayMagnifier != null) {
240 displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
242 // Not relevant for the window observer.
245 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
246 final int displayId = windowState.getDisplayId();
247 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
248 if (displayMagnifier != null) {
249 return displayMagnifier.getMagnificationSpecForWindowLocked(windowState);
254 public boolean hasCallbacksLocked() {
255 // TODO: support multi-display for windows observer
256 return (mDisplayMagnifiers.size() > 0
257 || mWindowsForAccessibilityObserver != null);
260 public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) {
261 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
262 if (displayMagnifier != null) {
263 displayMagnifier.setForceShowMagnifiableBoundsLocked(show);
264 displayMagnifier.showMagnificationBoundsIfNeeded();
268 private static void populateTransformationMatrixLocked(WindowState windowState,
270 windowState.getTransformationMatrix(sTempFloats, outMatrix);
274 * This class encapsulates the functionality related to display magnification.
276 private static final class DisplayMagnifier {
278 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
280 private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
281 private static final boolean DEBUG_ROTATION = false;
282 private static final boolean DEBUG_LAYERS = false;
283 private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
284 private static final boolean DEBUG_VIEWPORT_WINDOW = false;
286 private final Rect mTempRect1 = new Rect();
287 private final Rect mTempRect2 = new Rect();
289 private final Region mTempRegion1 = new Region();
290 private final Region mTempRegion2 = new Region();
291 private final Region mTempRegion3 = new Region();
292 private final Region mTempRegion4 = new Region();
294 private final Context mContext;
295 private final WindowManagerService mService;
296 private final MagnifiedViewport mMagnifedViewport;
297 private final Handler mHandler;
298 private final DisplayContent mDisplayContent;
299 private final Display mDisplay;
301 private final MagnificationCallbacks mCallbacks;
303 private final long mLongAnimationDuration;
305 private boolean mForceShowMagnifiableBounds = false;
307 public DisplayMagnifier(WindowManagerService windowManagerService,
308 DisplayContent displayContent,
310 MagnificationCallbacks callbacks) {
311 mContext = windowManagerService.mContext;
312 mService = windowManagerService;
313 mCallbacks = callbacks;
314 mDisplayContent = displayContent;
316 mHandler = new MyHandler(mService.mH.getLooper());
317 mMagnifedViewport = new MagnifiedViewport();
318 mLongAnimationDuration = mContext.getResources().getInteger(
319 com.android.internal.R.integer.config_longAnimTime);
322 public void setMagnificationSpecLocked(MagnificationSpec spec) {
323 mMagnifedViewport.updateMagnificationSpecLocked(spec);
324 mMagnifedViewport.recomputeBoundsLocked();
326 mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec);
327 mService.scheduleAnimationLocked();
330 public void setForceShowMagnifiableBoundsLocked(boolean show) {
331 mForceShowMagnifiableBounds = show;
332 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
335 public boolean isForceShowingMagnifiableBoundsLocked() {
336 return mForceShowMagnifiableBounds;
339 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
340 if (DEBUG_RECTANGLE_REQUESTED) {
341 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
343 if (!mMagnifedViewport.isMagnifyingLocked()) {
346 Rect magnifiedRegionBounds = mTempRect2;
347 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
348 if (magnifiedRegionBounds.contains(rectangle)) {
351 SomeArgs args = SomeArgs.obtain();
352 args.argi1 = rectangle.left;
353 args.argi2 = rectangle.top;
354 args.argi3 = rectangle.right;
355 args.argi4 = rectangle.bottom;
356 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
357 args).sendToTarget();
360 public void onWindowLayersChangedLocked() {
362 Slog.i(LOG_TAG, "Layers changed.");
364 mMagnifedViewport.recomputeBoundsLocked();
365 mService.scheduleAnimationLocked();
368 public void onRotationChangedLocked(DisplayContent displayContent) {
369 if (DEBUG_ROTATION) {
370 final int rotation = displayContent.getRotation();
371 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
372 + " displayId: " + displayContent.getDisplayId());
374 mMagnifedViewport.onRotationChangedLocked();
375 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
378 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
379 if (DEBUG_WINDOW_TRANSITIONS) {
380 Slog.i(LOG_TAG, "Window transition: "
381 + AppTransition.appTransitionToString(transition)
382 + " displayId: " + windowState.getDisplayId());
384 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
386 switch (transition) {
387 case WindowManager.TRANSIT_ACTIVITY_OPEN:
388 case WindowManager.TRANSIT_TASK_OPEN:
389 case WindowManager.TRANSIT_TASK_TO_FRONT:
390 case WindowManager.TRANSIT_WALLPAPER_OPEN:
391 case WindowManager.TRANSIT_WALLPAPER_CLOSE:
392 case WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN: {
393 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
399 public void onWindowTransitionLocked(WindowState windowState, int transition) {
400 if (DEBUG_WINDOW_TRANSITIONS) {
401 Slog.i(LOG_TAG, "Window transition: "
402 + AppTransition.appTransitionToString(transition)
403 + " displayId: " + windowState.getDisplayId());
405 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
406 final int type = windowState.mAttrs.type;
407 switch (transition) {
408 case WindowManagerPolicy.TRANSIT_ENTER:
409 case WindowManagerPolicy.TRANSIT_SHOW: {
414 case WindowManager.LayoutParams.TYPE_APPLICATION:
415 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
416 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
417 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
418 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
419 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
420 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
421 case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
422 case WindowManager.LayoutParams.TYPE_PHONE:
423 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
424 case WindowManager.LayoutParams.TYPE_TOAST:
425 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
426 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
427 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
428 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
429 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
430 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
431 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
432 case WindowManager.LayoutParams.TYPE_QS_DIALOG:
433 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
434 Rect magnifiedRegionBounds = mTempRect2;
435 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
436 magnifiedRegionBounds);
437 Rect touchableRegionBounds = mTempRect1;
438 windowState.getTouchableRegion(mTempRegion1);
439 mTempRegion1.getBounds(touchableRegionBounds);
440 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
441 mCallbacks.onRectangleOnScreenRequested(
442 touchableRegionBounds.left,
443 touchableRegionBounds.top,
444 touchableRegionBounds.right,
445 touchableRegionBounds.bottom);
453 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
454 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
455 if (spec != null && !spec.isNop()) {
456 if (!windowState.shouldMagnify()) {
463 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
464 // Make sure we're working with the most current bounds
465 mMagnifedViewport.recomputeBoundsLocked();
466 mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
469 public void destroyLocked() {
470 mMagnifedViewport.destroyWindow();
473 // Can be called outside of a surface transaction
474 public void showMagnificationBoundsIfNeeded() {
475 mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
479 /** NOTE: This has to be called within a surface transaction. */
480 public void drawMagnifiedRegionBorderIfNeededLocked() {
481 mMagnifedViewport.drawWindowIfNeededLocked();
484 private final class MagnifiedViewport {
486 private final SparseArray<WindowState> mTempWindowStates =
487 new SparseArray<WindowState>();
489 private final RectF mTempRectF = new RectF();
491 private final Point mTempPoint = new Point();
493 private final Matrix mTempMatrix = new Matrix();
495 private final Region mMagnificationRegion = new Region();
496 private final Region mOldMagnificationRegion = new Region();
498 private final Path mCircularPath;
500 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
502 private final WindowManager mWindowManager;
504 private final float mBorderWidth;
505 private final int mHalfBorderWidth;
506 private final int mDrawBorderInset;
508 private final ViewportWindow mWindow;
510 private boolean mFullRedrawNeeded;
511 private int mTempLayer = 0;
513 public MagnifiedViewport() {
514 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
515 mBorderWidth = mContext.getResources().getDimension(
516 com.android.internal.R.dimen.accessibility_magnification_indicator_width);
517 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
518 mDrawBorderInset = (int) mBorderWidth / 2;
519 mWindow = new ViewportWindow(mContext);
521 if (mContext.getResources().getConfiguration().isScreenRound()) {
522 mCircularPath = new Path();
523 mDisplay.getRealSize(mTempPoint);
524 final int centerXY = mTempPoint.x / 2;
525 mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
527 mCircularPath = null;
530 recomputeBoundsLocked();
533 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
534 outMagnificationRegion.set(mMagnificationRegion);
537 public void updateMagnificationSpecLocked(MagnificationSpec spec) {
539 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
541 mMagnificationSpec.clear();
543 // If this message is pending we are in a rotation animation and do not want
544 // to show the border. We will do so when the pending message is handled.
545 if (!mHandler.hasMessages(
546 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
547 setMagnifiedRegionBorderShownLocked(
548 isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
552 public void recomputeBoundsLocked() {
553 mDisplay.getRealSize(mTempPoint);
554 final int screenWidth = mTempPoint.x;
555 final int screenHeight = mTempPoint.y;
557 mMagnificationRegion.set(0, 0, 0, 0);
558 final Region availableBounds = mTempRegion1;
559 availableBounds.set(0, 0, screenWidth, screenHeight);
561 if (mCircularPath != null) {
562 availableBounds.setPath(mCircularPath, availableBounds);
565 Region nonMagnifiedBounds = mTempRegion4;
566 nonMagnifiedBounds.set(0, 0, 0, 0);
568 SparseArray<WindowState> visibleWindows = mTempWindowStates;
569 visibleWindows.clear();
570 populateWindowsOnScreenLocked(visibleWindows);
572 final int visibleWindowCount = visibleWindows.size();
573 for (int i = visibleWindowCount - 1; i >= 0; i--) {
574 WindowState windowState = visibleWindows.valueAt(i);
575 if ((windowState.mAttrs.type == TYPE_MAGNIFICATION_OVERLAY)
576 || ((windowState.mAttrs.privateFlags
577 & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
581 // Consider the touchable portion of the window
582 Matrix matrix = mTempMatrix;
583 populateTransformationMatrixLocked(windowState, matrix);
584 Region touchableRegion = mTempRegion3;
585 windowState.getTouchableRegion(touchableRegion);
586 Rect touchableFrame = mTempRect1;
587 touchableRegion.getBounds(touchableFrame);
588 RectF windowFrame = mTempRectF;
589 windowFrame.set(touchableFrame);
590 windowFrame.offset(-windowState.getFrameLw().left,
591 -windowState.getFrameLw().top);
592 matrix.mapRect(windowFrame);
593 Region windowBounds = mTempRegion2;
594 windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
595 (int) windowFrame.right, (int) windowFrame.bottom);
596 // Only update new regions
597 Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
598 portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
599 portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
600 windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
602 if (windowState.shouldMagnify()) {
603 mMagnificationRegion.op(windowBounds, Region.Op.UNION);
604 mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
606 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
607 availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
610 // Count letterbox into nonMagnifiedBounds
611 if (windowState.isLetterboxedForDisplayCutoutLw()) {
612 Region letterboxBounds = getLetterboxBounds(windowState);
613 nonMagnifiedBounds.op(letterboxBounds, Region.Op.UNION);
614 availableBounds.op(letterboxBounds, Region.Op.DIFFERENCE);
617 // Update accounted bounds
618 Region accountedBounds = mTempRegion2;
619 accountedBounds.set(mMagnificationRegion);
620 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
621 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
623 if (accountedBounds.isRect()) {
624 Rect accountedFrame = mTempRect1;
625 accountedBounds.getBounds(accountedFrame);
626 if (accountedFrame.width() == screenWidth
627 && accountedFrame.height() == screenHeight) {
633 visibleWindows.clear();
635 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
636 screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
637 Region.Op.INTERSECT);
639 final boolean magnifiedChanged =
640 !mOldMagnificationRegion.equals(mMagnificationRegion);
641 if (magnifiedChanged) {
642 mWindow.setBounds(mMagnificationRegion);
643 final Rect dirtyRect = mTempRect1;
644 if (mFullRedrawNeeded) {
645 mFullRedrawNeeded = false;
646 dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
647 screenWidth - mDrawBorderInset,
648 screenHeight - mDrawBorderInset);
649 mWindow.invalidate(dirtyRect);
651 final Region dirtyRegion = mTempRegion3;
652 dirtyRegion.set(mMagnificationRegion);
653 dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION);
654 dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
655 dirtyRegion.getBounds(dirtyRect);
656 mWindow.invalidate(dirtyRect);
659 mOldMagnificationRegion.set(mMagnificationRegion);
660 final SomeArgs args = SomeArgs.obtain();
661 args.arg1 = Region.obtain(mMagnificationRegion);
662 mHandler.obtainMessage(
663 MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
668 private Region getLetterboxBounds(WindowState windowState) {
669 final AppWindowToken appToken = windowState.mAppToken;
670 if (appToken == null) {
674 mDisplay.getRealSize(mTempPoint);
675 final Rect letterboxInsets = appToken.getLetterboxInsets();
676 final int screenWidth = mTempPoint.x;
677 final int screenHeight = mTempPoint.y;
678 final Rect nonLetterboxRect = mTempRect1;
679 final Region letterboxBounds = mTempRegion3;
680 nonLetterboxRect.set(0, 0, screenWidth, screenHeight);
681 nonLetterboxRect.inset(letterboxInsets);
682 letterboxBounds.set(0, 0, screenWidth, screenHeight);
683 letterboxBounds.op(nonLetterboxRect, Region.Op.DIFFERENCE);
684 return letterboxBounds;
687 public void onRotationChangedLocked() {
688 // If we are showing the magnification border, hide it immediately so
689 // the user does not see strange artifacts during rotation. The screenshot
690 // used for rotation already has the border. After the rotation is complete
691 // we will show the border.
692 if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
693 setMagnifiedRegionBorderShownLocked(false, false);
694 final long delay = (long) (mLongAnimationDuration
695 * mService.getWindowAnimationScaleLocked());
696 Message message = mHandler.obtainMessage(
697 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
698 mHandler.sendMessageDelayed(message, delay);
700 recomputeBoundsLocked();
701 mWindow.updateSize();
704 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
706 mFullRedrawNeeded = true;
707 mOldMagnificationRegion.set(0, 0, 0, 0);
709 mWindow.setShown(shown, animate);
712 public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
713 MagnificationSpec spec = mMagnificationSpec;
714 mMagnificationRegion.getBounds(rect);
715 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
716 rect.scale(1.0f / spec.scale);
719 public boolean isMagnifyingLocked() {
720 return mMagnificationSpec.scale > 1.0f;
723 public MagnificationSpec getMagnificationSpecLocked() {
724 return mMagnificationSpec;
727 /** NOTE: This has to be called within a surface transaction. */
728 public void drawWindowIfNeededLocked() {
729 recomputeBoundsLocked();
730 mWindow.drawIfNeeded();
733 public void destroyWindow() {
734 mWindow.releaseSurface();
737 private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
739 mDisplayContent.forAllWindows((w) -> {
740 if (w.isOnScreen() && w.isVisibleLw()
741 && (w.mAttrs.alpha != 0)
742 && !w.mWinAnimator.mEnterAnimationPending) {
744 outWindows.put(mTempLayer, w);
746 }, false /* traverseTopToBottom */ );
749 private final class ViewportWindow {
750 private static final String SURFACE_TITLE = "Magnification Overlay";
752 private final Region mBounds = new Region();
753 private final Rect mDirtyRect = new Rect();
754 private final Paint mPaint = new Paint();
756 private final SurfaceControl mSurfaceControl;
757 private final Surface mSurface = new Surface();
759 private final AnimationController mAnimationController;
761 private boolean mShown;
764 private boolean mInvalidated;
766 public ViewportWindow(Context context) {
767 SurfaceControl surfaceControl = null;
769 mDisplay.getRealSize(mTempPoint);
770 surfaceControl = mDisplayContent
772 .setName(SURFACE_TITLE)
773 .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo
774 .setFormat(PixelFormat.TRANSLUCENT)
776 } catch (OutOfResourcesException oore) {
779 mSurfaceControl = surfaceControl;
780 mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
781 TYPE_MAGNIFICATION_OVERLAY)
782 * WindowManagerService.TYPE_LAYER_MULTIPLIER);
783 mSurfaceControl.setPosition(0, 0);
784 mSurface.copyFrom(mSurfaceControl);
786 mAnimationController = new AnimationController(context,
787 mService.mH.getLooper());
789 TypedValue typedValue = new TypedValue();
790 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
792 final int borderColor = context.getColor(typedValue.resourceId);
794 mPaint.setStyle(Paint.Style.STROKE);
795 mPaint.setStrokeWidth(mBorderWidth);
796 mPaint.setColor(borderColor);
801 public void setShown(boolean shown, boolean animate) {
802 synchronized (mService.mGlobalLock) {
803 if (mShown == shown) {
807 mAnimationController.onFrameShownStateChanged(shown, animate);
808 if (DEBUG_VIEWPORT_WINDOW) {
809 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
814 @SuppressWarnings("unused")
815 // Called reflectively from an animator.
816 public int getAlpha() {
817 synchronized (mService.mGlobalLock) {
822 public void setAlpha(int alpha) {
823 synchronized (mService.mGlobalLock) {
824 if (mAlpha == alpha) {
829 if (DEBUG_VIEWPORT_WINDOW) {
830 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
835 public void setBounds(Region bounds) {
836 synchronized (mService.mGlobalLock) {
837 if (mBounds.equals(bounds)) {
841 invalidate(mDirtyRect);
842 if (DEBUG_VIEWPORT_WINDOW) {
843 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
848 public void updateSize() {
849 synchronized (mService.mGlobalLock) {
850 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
851 mSurfaceControl.setBufferSize(mTempPoint.x, mTempPoint.y);
852 invalidate(mDirtyRect);
856 public void invalidate(Rect dirtyRect) {
857 if (dirtyRect != null) {
858 mDirtyRect.set(dirtyRect);
860 mDirtyRect.setEmpty();
863 mService.scheduleAnimationLocked();
866 /** NOTE: This has to be called within a surface transaction. */
867 public void drawIfNeeded() {
868 synchronized (mService.mGlobalLock) {
872 mInvalidated = false;
874 Canvas canvas = null;
876 // Empty dirty rectangle means unspecified.
877 if (mDirtyRect.isEmpty()) {
878 mBounds.getBounds(mDirtyRect);
880 mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth);
881 canvas = mSurface.lockCanvas(mDirtyRect);
882 if (DEBUG_VIEWPORT_WINDOW) {
883 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
885 } catch (IllegalArgumentException iae) {
887 } catch (Surface.OutOfResourcesException oore) {
890 if (canvas == null) {
893 if (DEBUG_VIEWPORT_WINDOW) {
894 Slog.i(LOG_TAG, "Bounds: " + mBounds);
896 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
897 mPaint.setAlpha(mAlpha);
898 Path path = mBounds.getBoundaryPath();
899 canvas.drawPath(path, mPaint);
901 mSurface.unlockCanvasAndPost(canvas);
902 mSurfaceControl.show();
904 mSurfaceControl.hide();
909 public void releaseSurface() {
910 mSurfaceControl.remove();
914 private final class AnimationController extends Handler {
915 private static final String PROPERTY_NAME_ALPHA = "alpha";
917 private static final int MIN_ALPHA = 0;
918 private static final int MAX_ALPHA = 255;
920 private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
922 private final ValueAnimator mShowHideFrameAnimator;
924 public AnimationController(Context context, Looper looper) {
926 mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
927 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
929 Interpolator interpolator = new DecelerateInterpolator(2.5f);
930 final long longAnimationDuration = context.getResources().getInteger(
931 com.android.internal.R.integer.config_longAnimTime);
933 mShowHideFrameAnimator.setInterpolator(interpolator);
934 mShowHideFrameAnimator.setDuration(longAnimationDuration);
937 public void onFrameShownStateChanged(boolean shown, boolean animate) {
938 obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
939 shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
943 public void handleMessage(Message message) {
944 switch (message.what) {
945 case MSG_FRAME_SHOWN_STATE_CHANGED: {
946 final boolean shown = message.arg1 == 1;
947 final boolean animate = message.arg2 == 1;
950 if (mShowHideFrameAnimator.isRunning()) {
951 mShowHideFrameAnimator.reverse();
954 mShowHideFrameAnimator.start();
956 mShowHideFrameAnimator.reverse();
960 mShowHideFrameAnimator.cancel();
974 private class MyHandler extends Handler {
975 public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
976 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
977 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
978 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
979 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
981 public MyHandler(Looper looper) {
986 public void handleMessage(Message message) {
987 switch (message.what) {
988 case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
989 final SomeArgs args = (SomeArgs) message.obj;
990 final Region magnifiedBounds = (Region) args.arg1;
991 mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
992 magnifiedBounds.recycle();
995 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
996 SomeArgs args = (SomeArgs) message.obj;
997 final int left = args.argi1;
998 final int top = args.argi2;
999 final int right = args.argi3;
1000 final int bottom = args.argi4;
1001 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
1005 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
1006 mCallbacks.onUserContextChanged();
1009 case MESSAGE_NOTIFY_ROTATION_CHANGED: {
1010 final int rotation = message.arg1;
1011 mCallbacks.onRotationChanged(rotation);
1014 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
1015 synchronized (mService.mGlobalLock) {
1016 if (mMagnifedViewport.isMagnifyingLocked()
1017 || isForceShowingMagnifiableBoundsLocked()) {
1018 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
1019 mService.scheduleAnimationLocked();
1029 * This class encapsulates the functionality related to computing the windows
1030 * reported for accessibility purposes. These windows are all windows a sighted
1031 * user can see on the screen.
1033 private static final class WindowsForAccessibilityObserver {
1034 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ?
1035 "WindowsForAccessibilityObserver" : TAG_WM;
1037 private static final boolean DEBUG = false;
1039 private final SparseArray<WindowState> mTempWindowStates =
1040 new SparseArray<WindowState>();
1042 private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
1044 private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
1046 private final RectF mTempRectF = new RectF();
1048 private final Matrix mTempMatrix = new Matrix();
1050 private final Point mTempPoint = new Point();
1052 private final Rect mTempRect = new Rect();
1054 private final Region mTempRegion = new Region();
1056 private final Region mTempRegion1 = new Region();
1058 private final Context mContext;
1060 private final WindowManagerService mService;
1062 private final Handler mHandler;
1064 private final WindowsForAccessibilityCallback mCallback;
1066 private final long mRecurringAccessibilityEventsIntervalMillis;
1068 public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
1069 WindowsForAccessibilityCallback callback) {
1070 mContext = windowManagerService.mContext;
1071 mService = windowManagerService;
1072 mCallback = callback;
1073 mHandler = new MyHandler(mService.mH.getLooper());
1074 mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
1075 .getSendRecurringAccessibilityEventsInterval();
1076 computeChangedWindows(true);
1079 public void performComputeChangedWindowsNotLocked(boolean forceSend) {
1080 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
1081 computeChangedWindows(forceSend);
1084 public void scheduleComputeChangedWindowsLocked() {
1085 if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
1086 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
1087 mRecurringAccessibilityEventsIntervalMillis);
1092 * Check if windows have changed, and send them to the accessibility subsystem if they have.
1094 * @param forceSend Send the windows the accessibility even if they haven't changed.
1096 public void computeChangedWindows(boolean forceSend) {
1098 Slog.i(LOG_TAG, "computeChangedWindows()");
1101 boolean windowsChanged = false;
1102 List<WindowInfo> windows = new ArrayList<WindowInfo>();
1104 synchronized (mService.mGlobalLock) {
1105 // Do not send the windows if there is no current focus as
1106 // the window manager is still looking for where to put it.
1107 // We will do the work when we get a focus change callback.
1108 // TODO(b/112273690): Support multiple displays
1109 if (!isCurrentFocusWindowOnDefaultDisplay()) {
1113 WindowManager windowManager = (WindowManager)
1114 mContext.getSystemService(Context.WINDOW_SERVICE);
1115 windowManager.getDefaultDisplay().getRealSize(mTempPoint);
1116 final int screenWidth = mTempPoint.x;
1117 final int screenHeight = mTempPoint.y;
1119 Region unaccountedSpace = mTempRegion;
1120 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
1122 final SparseArray<WindowState> visibleWindows = mTempWindowStates;
1123 populateVisibleWindowsOnScreenLocked(visibleWindows);
1124 Set<IBinder> addedWindows = mTempBinderSet;
1125 addedWindows.clear();
1127 boolean focusedWindowAdded = false;
1129 final int visibleWindowCount = visibleWindows.size();
1130 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
1132 // Iterate until we figure out what is touchable for the entire screen.
1133 for (int i = visibleWindowCount - 1; i >= 0; i--) {
1134 final WindowState windowState = visibleWindows.valueAt(i);
1136 final Rect boundsInScreen = mTempRect;
1137 computeWindowBoundsInScreen(windowState, boundsInScreen);
1139 if (windowMattersToAccessibility(windowState, boundsInScreen, unaccountedSpace,
1140 skipRemainingWindowsForTasks)) {
1141 addPopulatedWindowInfo(windowState, boundsInScreen, windows, addedWindows);
1142 updateUnaccountedSpace(windowState, boundsInScreen, unaccountedSpace,
1143 skipRemainingWindowsForTasks);
1144 focusedWindowAdded |= windowState.isFocused();
1147 if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
1152 // Remove child/parent references to windows that were not added.
1153 final int windowCount = windows.size();
1154 for (int i = 0; i < windowCount; i++) {
1155 WindowInfo window = windows.get(i);
1156 if (!addedWindows.contains(window.parentToken)) {
1157 window.parentToken = null;
1159 if (window.childTokens != null) {
1160 final int childTokenCount = window.childTokens.size();
1161 for (int j = childTokenCount - 1; j >= 0; j--) {
1162 if (!addedWindows.contains(window.childTokens.get(j))) {
1163 window.childTokens.remove(j);
1166 // Leave the child token list if empty.
1170 visibleWindows.clear();
1171 addedWindows.clear();
1174 // We computed the windows and if they changed notify the client.
1175 if (mOldWindows.size() != windows.size()) {
1176 // Different size means something changed.
1177 windowsChanged = true;
1178 } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
1179 // Since we always traverse windows from high to low layer
1180 // the old and new windows at the same index should be the
1181 // same, otherwise something changed.
1182 for (int i = 0; i < windowCount; i++) {
1183 WindowInfo oldWindow = mOldWindows.get(i);
1184 WindowInfo newWindow = windows.get(i);
1185 // We do not care for layer changes given the window
1186 // order does not change. This brings no new information
1188 if (windowChangedNoLayer(oldWindow, newWindow)) {
1189 windowsChanged = true;
1196 if (forceSend || windowsChanged) {
1197 cacheWindows(windows);
1201 // Now we do not hold the lock, so send the windows over.
1202 if (forceSend || windowsChanged) {
1204 Log.i(LOG_TAG, "Windows changed or force sending:" + windows);
1206 mCallback.onWindowsForAccessibilityChanged(windows);
1209 Log.i(LOG_TAG, "No windows changed.");
1213 // Recycle the windows as we do not need them.
1214 clearAndRecycleWindows(windows);
1217 private boolean windowMattersToAccessibility(WindowState windowState, Rect boundsInScreen,
1218 Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
1219 if (windowState.isFocused()) {
1223 // If the window is part of a task that we're finished with - ignore.
1224 final Task task = windowState.getTask();
1225 if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
1229 // Ignore non-touchable windows, except the split-screen divider, which is
1230 // occasionally non-touchable but still useful for identifying split-screen
1232 if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
1233 && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
1237 // If the window is completely covered by other windows - ignore.
1238 if (unaccountedSpace.quickReject(boundsInScreen)) {
1242 // Add windows of certain types not covered by modal windows.
1243 if (isReportedWindowType(windowState.mAttrs.type)) {
1250 private void updateUnaccountedSpace(WindowState windowState, Rect boundsInScreen,
1251 Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
1252 if (windowState.mAttrs.type
1253 != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
1255 // Account for the space this window takes if the window
1256 // is not an accessibility overlay which does not change
1257 // the reported windows.
1258 unaccountedSpace.op(boundsInScreen, unaccountedSpace,
1259 Region.Op.REVERSE_DIFFERENCE);
1261 // If a window is modal it prevents other windows from being touched
1262 if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1263 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
1264 // Account for all space in the task, whether the windows in it are
1265 // touchable or not. The modal window blocks all touches from the task's
1267 unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
1268 Region.Op.REVERSE_DIFFERENCE);
1270 final Task task = windowState.getTask();
1272 // If the window is associated with a particular task, we can skip the
1273 // rest of the windows for that task.
1274 skipRemainingWindowsForTasks.add(task.mTaskId);
1276 // If the window is not associated with a particular task, then it is
1277 // globally modal. In this case we can skip all remaining windows.
1278 unaccountedSpace.setEmpty();
1284 private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
1285 // Get the touchable frame.
1286 Region touchableRegion = mTempRegion1;
1287 windowState.getTouchableRegion(touchableRegion);
1288 Rect touchableFrame = mTempRect;
1289 touchableRegion.getBounds(touchableFrame);
1291 // Move to origin as all transforms are captured by the matrix.
1292 RectF windowFrame = mTempRectF;
1293 windowFrame.set(touchableFrame);
1294 windowFrame.offset(-windowState.getFrameLw().left, -windowState.getFrameLw().top);
1296 // Map the frame to get what appears on the screen.
1297 Matrix matrix = mTempMatrix;
1298 populateTransformationMatrixLocked(windowState, matrix);
1299 matrix.mapRect(windowFrame);
1302 outBounds.set((int) windowFrame.left, (int) windowFrame.top,
1303 (int) windowFrame.right, (int) windowFrame.bottom);
1306 private static void addPopulatedWindowInfo(
1307 WindowState windowState, Rect boundsInScreen,
1308 List<WindowInfo> out, Set<IBinder> tokenOut) {
1309 final WindowInfo window = windowState.getWindowInfo();
1310 window.boundsInScreen.set(boundsInScreen);
1311 window.layer = tokenOut.size();
1313 tokenOut.add(window.token);
1316 private void cacheWindows(List<WindowInfo> windows) {
1317 final int oldWindowCount = mOldWindows.size();
1318 for (int i = oldWindowCount - 1; i >= 0; i--) {
1319 mOldWindows.remove(i).recycle();
1321 final int newWindowCount = windows.size();
1322 for (int i = 0; i < newWindowCount; i++) {
1323 WindowInfo newWindow = windows.get(i);
1324 mOldWindows.add(WindowInfo.obtain(newWindow));
1328 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
1329 if (oldWindow == newWindow) {
1332 if (oldWindow == null) {
1335 if (newWindow == null) {
1338 if (oldWindow.type != newWindow.type) {
1341 if (oldWindow.focused != newWindow.focused) {
1344 if (oldWindow.token == null) {
1345 if (newWindow.token != null) {
1348 } else if (!oldWindow.token.equals(newWindow.token)) {
1351 if (oldWindow.parentToken == null) {
1352 if (newWindow.parentToken != null) {
1355 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
1358 if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
1361 if (oldWindow.childTokens != null && newWindow.childTokens != null
1362 && !oldWindow.childTokens.equals(newWindow.childTokens)) {
1365 if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
1368 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
1374 private static void clearAndRecycleWindows(List<WindowInfo> windows) {
1375 final int windowCount = windows.size();
1376 for (int i = windowCount - 1; i >= 0; i--) {
1377 windows.remove(i).recycle();
1381 private static boolean isReportedWindowType(int windowType) {
1382 return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
1383 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
1384 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
1385 && windowType != WindowManager.LayoutParams.TYPE_DRAG
1386 && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
1387 && windowType != WindowManager.LayoutParams.TYPE_POINTER
1388 && windowType != TYPE_MAGNIFICATION_OVERLAY
1389 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
1390 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
1391 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
1394 private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
1395 final List<WindowState> tempWindowStatesList = new ArrayList<>();
1396 final DisplayContent dc = mService.getDefaultDisplayContentLocked();
1397 dc.forAllWindows((w) -> {
1398 if (w.isVisibleLw()) {
1399 tempWindowStatesList.add(w);
1401 }, false /* traverseTopToBottom */);
1402 // Insert the re-parented windows in another display on top of their parents in
1404 mService.mRoot.forAllWindows(w -> {
1405 final WindowState parentWindow = findRootDisplayParentWindow(w);
1406 if (parentWindow == null) {
1410 // TODO: Use Region instead to get rid of this complicated logic.
1411 // Check the tap exclude region of the parent window. If the tap exclude region
1412 // is empty, it means there is another can-receive-pointer-event view on top of
1413 // the region. Hence, we don't count the window as visible.
1414 if (w.isVisibleLw() && parentWindow.getDisplayContent().isDefaultDisplay
1415 && parentWindow.hasTapExcludeRegion()
1416 && tempWindowStatesList.contains(parentWindow)) {
1417 tempWindowStatesList.add(
1418 tempWindowStatesList.lastIndexOf(parentWindow) + 1, w);
1420 }, true /* traverseTopToBottom */);
1421 for (int i = 0; i < tempWindowStatesList.size(); i++) {
1422 outWindows.put(i, tempWindowStatesList.get(i));
1426 private WindowState findRootDisplayParentWindow(WindowState win) {
1427 WindowState displayParentWindow = win.getDisplayContent().getParentWindow();
1428 if (displayParentWindow == null) {
1431 WindowState candidate = displayParentWindow;
1432 while (candidate != null) {
1433 displayParentWindow = candidate;
1434 candidate = displayParentWindow.getDisplayContent().getParentWindow();
1436 return displayParentWindow;
1439 private boolean isCurrentFocusWindowOnDefaultDisplay() {
1440 final WindowState focusedWindow =
1441 mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
1442 if (focusedWindow == null) {
1446 final WindowState rootDisplayParentWindow = findRootDisplayParentWindow(focusedWindow);
1447 if (!focusedWindow.isDefaultDisplay()
1448 && (rootDisplayParentWindow == null
1449 || !rootDisplayParentWindow.isDefaultDisplay())) {
1456 private class MyHandler extends Handler {
1457 public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
1459 public MyHandler(Looper looper) {
1460 super(looper, null, false);
1464 @SuppressWarnings("unchecked")
1465 public void handleMessage(Message message) {
1466 switch (message.what) {
1467 case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
1468 computeChangedWindows(false);