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 com.android.server.wm; 18 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; 22 23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 25 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; 62 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; 68 69 import java.util.ArrayList; 70 import java.util.HashSet; 71 import java.util.List; 72 import java.util.Set; 73 74 /** 75 * This class contains the accessibility related logic of the window manager. 76 */ 77 final class AccessibilityController { 78 79 private final WindowManagerService mService; 80 81 private static final float[] sTempFloats = new float[9]; 82 AccessibilityController(WindowManagerService service)83 public AccessibilityController(WindowManagerService service) { 84 mService = service; 85 } 86 87 private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>(); 88 89 private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver; 90 setMagnificationCallbacksLocked(int displayId, MagnificationCallbacks callbacks)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!"); 97 } 98 final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); 99 if (dc != null) { 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)); 104 result = true; 105 } 106 } 107 } else { 108 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 109 if (displayMagnifier == null) { 110 throw new IllegalStateException("Magnification callbacks already cleared!"); 111 } 112 displayMagnifier.destroyLocked(); 113 mDisplayMagnifiers.remove(displayId); 114 result = true; 115 } 116 return result; 117 } 118 setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback)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!"); 124 } 125 mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver( 126 mService, callback); 127 } else { 128 if (mWindowsForAccessibilityObserver == null) { 129 throw new IllegalStateException( 130 "Windows for accessibility callback already cleared!"); 131 } 132 mWindowsForAccessibilityObserver = null; 133 } 134 } 135 performComputeChangedWindowsNotLocked(boolean forceSend)136 public void performComputeChangedWindowsNotLocked(boolean forceSend) { 137 WindowsForAccessibilityObserver observer = null; 138 synchronized (mService) { 139 observer = mWindowsForAccessibilityObserver; 140 } 141 if (observer != null) { 142 observer.performComputeChangedWindowsNotLocked(forceSend); 143 } 144 } 145 setMagnificationSpecLocked(int displayId, MagnificationSpec spec)146 public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) { 147 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 148 if (displayMagnifier != null) { 149 displayMagnifier.setMagnificationSpecLocked(spec); 150 } 151 // TODO: support multi-display for windows observer 152 if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { 153 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 154 } 155 } 156 getMagnificationRegionLocked(int displayId, Region outMagnificationRegion)157 public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) { 158 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 159 if (displayMagnifier != null) { 160 displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion); 161 } 162 } 163 onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle)164 public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) { 165 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 166 if (displayMagnifier != null) { 167 displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle); 168 } 169 // Not relevant for the window observer. 170 } 171 onWindowLayersChangedLocked(int displayId)172 public void onWindowLayersChangedLocked(int displayId) { 173 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 174 if (displayMagnifier != null) { 175 displayMagnifier.onWindowLayersChangedLocked(); 176 } 177 // TODO: support multi-display for windows observer 178 if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { 179 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 180 } 181 } 182 onRotationChangedLocked(DisplayContent displayContent)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); 188 } 189 // TODO: support multi-display for windows observer 190 if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { 191 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 192 } 193 } 194 onAppWindowTransitionLocked(WindowState windowState, int transition)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); 200 } 201 // Not relevant for the window observer. 202 } 203 onWindowTransitionLocked(WindowState windowState, int transition)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); 209 } 210 // TODO: support multi-display for windows observer 211 if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { 212 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 213 } 214 } 215 onWindowFocusChangedNotLocked()216 public void onWindowFocusChangedNotLocked() { 217 // Not relevant for the display magnifier. 218 219 WindowsForAccessibilityObserver observer = null; 220 synchronized (mService) { 221 observer = mWindowsForAccessibilityObserver; 222 } 223 if (observer != null) { 224 observer.performComputeChangedWindowsNotLocked(false); 225 } 226 } 227 onSomeWindowResizedOrMovedLocked()228 public void onSomeWindowResizedOrMovedLocked() { 229 // Not relevant for the display magnifier. 230 231 if (mWindowsForAccessibilityObserver != null) { 232 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 233 } 234 } 235 236 /** NOTE: This has to be called within a surface transaction. */ drawMagnifiedRegionBorderIfNeededLocked(int displayId)237 public void drawMagnifiedRegionBorderIfNeededLocked(int displayId) { 238 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 239 if (displayMagnifier != null) { 240 displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(); 241 } 242 // Not relevant for the window observer. 243 } 244 getMagnificationSpecForWindowLocked(WindowState windowState)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); 250 } 251 return null; 252 } 253 hasCallbacksLocked()254 public boolean hasCallbacksLocked() { 255 // TODO: support multi-display for windows observer 256 return (mDisplayMagnifiers.size() > 0 257 || mWindowsForAccessibilityObserver != null); 258 } 259 setForceShowMagnifiableBoundsLocked(int displayId, boolean show)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(); 265 } 266 } 267 populateTransformationMatrixLocked(WindowState windowState, Matrix outMatrix)268 private static void populateTransformationMatrixLocked(WindowState windowState, 269 Matrix outMatrix) { 270 windowState.getTransformationMatrix(sTempFloats, outMatrix); 271 } 272 273 /** 274 * This class encapsulates the functionality related to display magnification. 275 */ 276 private static final class DisplayMagnifier { 277 278 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM; 279 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; 285 286 private final Rect mTempRect1 = new Rect(); 287 private final Rect mTempRect2 = new Rect(); 288 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(); 293 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; 300 301 private final MagnificationCallbacks mCallbacks; 302 303 private final long mLongAnimationDuration; 304 305 private boolean mForceShowMagnifiableBounds = false; 306 DisplayMagnifier(WindowManagerService windowManagerService, DisplayContent displayContent, Display display, MagnificationCallbacks callbacks)307 public DisplayMagnifier(WindowManagerService windowManagerService, 308 DisplayContent displayContent, 309 Display display, 310 MagnificationCallbacks callbacks) { 311 mContext = windowManagerService.mContext; 312 mService = windowManagerService; 313 mCallbacks = callbacks; 314 mDisplayContent = displayContent; 315 mDisplay = display; 316 mHandler = new MyHandler(mService.mH.getLooper()); 317 mMagnifedViewport = new MagnifiedViewport(); 318 mLongAnimationDuration = mContext.getResources().getInteger( 319 com.android.internal.R.integer.config_longAnimTime); 320 } 321 setMagnificationSpecLocked(MagnificationSpec spec)322 public void setMagnificationSpecLocked(MagnificationSpec spec) { 323 mMagnifedViewport.updateMagnificationSpecLocked(spec); 324 mMagnifedViewport.recomputeBoundsLocked(); 325 326 mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec); 327 mService.scheduleAnimationLocked(); 328 } 329 setForceShowMagnifiableBoundsLocked(boolean show)330 public void setForceShowMagnifiableBoundsLocked(boolean show) { 331 mForceShowMagnifiableBounds = show; 332 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true); 333 } 334 isForceShowingMagnifiableBoundsLocked()335 public boolean isForceShowingMagnifiableBoundsLocked() { 336 return mForceShowMagnifiableBounds; 337 } 338 onRectangleOnScreenRequestedLocked(Rect rectangle)339 public void onRectangleOnScreenRequestedLocked(Rect rectangle) { 340 if (DEBUG_RECTANGLE_REQUESTED) { 341 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); 342 } 343 if (!mMagnifedViewport.isMagnifyingLocked()) { 344 return; 345 } 346 Rect magnifiedRegionBounds = mTempRect2; 347 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds); 348 if (magnifiedRegionBounds.contains(rectangle)) { 349 return; 350 } 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(); 358 } 359 onWindowLayersChangedLocked()360 public void onWindowLayersChangedLocked() { 361 if (DEBUG_LAYERS) { 362 Slog.i(LOG_TAG, "Layers changed."); 363 } 364 mMagnifedViewport.recomputeBoundsLocked(); 365 mService.scheduleAnimationLocked(); 366 } 367 onRotationChangedLocked(DisplayContent displayContent)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()); 373 } 374 mMagnifedViewport.onRotationChangedLocked(); 375 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED); 376 } 377 onAppWindowTransitionLocked(WindowState windowState, int transition)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()); 383 } 384 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); 385 if (magnifying) { 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); 394 } 395 } 396 } 397 } 398 onWindowTransitionLocked(WindowState windowState, int transition)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()); 404 } 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: { 410 if (!magnifying) { 411 break; 412 } 413 switch (type) { 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); 446 } 447 } break; 448 } break; 449 } 450 } 451 } 452 getMagnificationSpecForWindowLocked(WindowState windowState)453 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { 454 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked(); 455 if (spec != null && !spec.isNop()) { 456 if (!windowState.shouldMagnify()) { 457 return null; 458 } 459 } 460 return spec; 461 } 462 getMagnificationRegionLocked(Region outMagnificationRegion)463 public void getMagnificationRegionLocked(Region outMagnificationRegion) { 464 // Make sure we're working with the most current bounds 465 mMagnifedViewport.recomputeBoundsLocked(); 466 mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion); 467 } 468 destroyLocked()469 public void destroyLocked() { 470 mMagnifedViewport.destroyWindow(); 471 } 472 473 // Can be called outside of a surface transaction showMagnificationBoundsIfNeeded()474 public void showMagnificationBoundsIfNeeded() { 475 mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED) 476 .sendToTarget(); 477 } 478 479 /** NOTE: This has to be called within a surface transaction. */ drawMagnifiedRegionBorderIfNeededLocked()480 public void drawMagnifiedRegionBorderIfNeededLocked() { 481 mMagnifedViewport.drawWindowIfNeededLocked(); 482 } 483 484 private final class MagnifiedViewport { 485 486 private final SparseArray<WindowState> mTempWindowStates = 487 new SparseArray<WindowState>(); 488 489 private final RectF mTempRectF = new RectF(); 490 491 private final Point mTempPoint = new Point(); 492 493 private final Matrix mTempMatrix = new Matrix(); 494 495 private final Region mMagnificationRegion = new Region(); 496 private final Region mOldMagnificationRegion = new Region(); 497 498 private final Path mCircularPath; 499 500 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain(); 501 502 private final WindowManager mWindowManager; 503 504 private final float mBorderWidth; 505 private final int mHalfBorderWidth; 506 private final int mDrawBorderInset; 507 508 private final ViewportWindow mWindow; 509 510 private boolean mFullRedrawNeeded; 511 private int mTempLayer = 0; 512 MagnifiedViewport()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); 520 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); 526 } else { 527 mCircularPath = null; 528 } 529 530 recomputeBoundsLocked(); 531 } 532 getMagnificationRegionLocked(@onNull Region outMagnificationRegion)533 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) { 534 outMagnificationRegion.set(mMagnificationRegion); 535 } 536 updateMagnificationSpecLocked(MagnificationSpec spec)537 public void updateMagnificationSpecLocked(MagnificationSpec spec) { 538 if (spec != null) { 539 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY); 540 } else { 541 mMagnificationSpec.clear(); 542 } 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); 549 } 550 } 551 recomputeBoundsLocked()552 public void recomputeBoundsLocked() { 553 mDisplay.getRealSize(mTempPoint); 554 final int screenWidth = mTempPoint.x; 555 final int screenHeight = mTempPoint.y; 556 557 mMagnificationRegion.set(0, 0, 0, 0); 558 final Region availableBounds = mTempRegion1; 559 availableBounds.set(0, 0, screenWidth, screenHeight); 560 561 if (mCircularPath != null) { 562 availableBounds.setPath(mCircularPath, availableBounds); 563 } 564 565 Region nonMagnifiedBounds = mTempRegion4; 566 nonMagnifiedBounds.set(0, 0, 0, 0); 567 568 SparseArray<WindowState> visibleWindows = mTempWindowStates; 569 visibleWindows.clear(); 570 populateWindowsOnScreenLocked(visibleWindows); 571 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)) { 578 continue; 579 } 580 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); 601 602 if (windowState.shouldMagnify()) { 603 mMagnificationRegion.op(windowBounds, Region.Op.UNION); 604 mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT); 605 } else { 606 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION); 607 availableBounds.op(windowBounds, Region.Op.DIFFERENCE); 608 } 609 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); 615 } 616 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); 622 623 if (accountedBounds.isRect()) { 624 Rect accountedFrame = mTempRect1; 625 accountedBounds.getBounds(accountedFrame); 626 if (accountedFrame.width() == screenWidth 627 && accountedFrame.height() == screenHeight) { 628 break; 629 } 630 } 631 } 632 633 visibleWindows.clear(); 634 635 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset, 636 screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset, 637 Region.Op.INTERSECT); 638 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); 650 } else { 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); 657 } 658 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) 664 .sendToTarget(); 665 } 666 } 667 getLetterboxBounds(WindowState windowState)668 private Region getLetterboxBounds(WindowState windowState) { 669 final AppWindowToken appToken = windowState.mAppToken; 670 if (appToken == null) { 671 return new Region(); 672 } 673 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; 685 } 686 onRotationChangedLocked()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); 699 } 700 recomputeBoundsLocked(); 701 mWindow.updateSize(); 702 } 703 setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate)704 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) { 705 if (shown) { 706 mFullRedrawNeeded = true; 707 mOldMagnificationRegion.set(0, 0, 0, 0); 708 } 709 mWindow.setShown(shown, animate); 710 } 711 getMagnifiedFrameInContentCoordsLocked(Rect rect)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); 717 } 718 isMagnifyingLocked()719 public boolean isMagnifyingLocked() { 720 return mMagnificationSpec.scale > 1.0f; 721 } 722 getMagnificationSpecLocked()723 public MagnificationSpec getMagnificationSpecLocked() { 724 return mMagnificationSpec; 725 } 726 727 /** NOTE: This has to be called within a surface transaction. */ drawWindowIfNeededLocked()728 public void drawWindowIfNeededLocked() { 729 recomputeBoundsLocked(); 730 mWindow.drawIfNeeded(); 731 } 732 destroyWindow()733 public void destroyWindow() { 734 mWindow.releaseSurface(); 735 } 736 populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows)737 private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { 738 mTempLayer = 0; 739 mDisplayContent.forAllWindows((w) -> { 740 if (w.isOnScreen() && w.isVisibleLw() 741 && (w.mAttrs.alpha != 0) 742 && !w.mWinAnimator.mEnterAnimationPending) { 743 mTempLayer++; 744 outWindows.put(mTempLayer, w); 745 } 746 }, false /* traverseTopToBottom */ ); 747 } 748 749 private final class ViewportWindow { 750 private static final String SURFACE_TITLE = "Magnification Overlay"; 751 752 private final Region mBounds = new Region(); 753 private final Rect mDirtyRect = new Rect(); 754 private final Paint mPaint = new Paint(); 755 756 private final SurfaceControl mSurfaceControl; 757 private final Surface mSurface = new Surface(); 758 759 private final AnimationController mAnimationController; 760 761 private boolean mShown; 762 private int mAlpha; 763 764 private boolean mInvalidated; 765 ViewportWindow(Context context)766 public ViewportWindow(Context context) { 767 SurfaceControl surfaceControl = null; 768 try { 769 mDisplay.getRealSize(mTempPoint); 770 surfaceControl = mDisplayContent 771 .makeOverlay() 772 .setName(SURFACE_TITLE) 773 .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo 774 .setFormat(PixelFormat.TRANSLUCENT) 775 .build(); 776 } catch (OutOfResourcesException oore) { 777 /* ignore */ 778 } 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); 785 786 mAnimationController = new AnimationController(context, 787 mService.mH.getLooper()); 788 789 TypedValue typedValue = new TypedValue(); 790 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight, 791 typedValue, true); 792 final int borderColor = context.getColor(typedValue.resourceId); 793 794 mPaint.setStyle(Paint.Style.STROKE); 795 mPaint.setStrokeWidth(mBorderWidth); 796 mPaint.setColor(borderColor); 797 798 mInvalidated = true; 799 } 800 setShown(boolean shown, boolean animate)801 public void setShown(boolean shown, boolean animate) { 802 synchronized (mService.mGlobalLock) { 803 if (mShown == shown) { 804 return; 805 } 806 mShown = shown; 807 mAnimationController.onFrameShownStateChanged(shown, animate); 808 if (DEBUG_VIEWPORT_WINDOW) { 809 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown); 810 } 811 } 812 } 813 814 @SuppressWarnings("unused") 815 // Called reflectively from an animator. getAlpha()816 public int getAlpha() { 817 synchronized (mService.mGlobalLock) { 818 return mAlpha; 819 } 820 } 821 setAlpha(int alpha)822 public void setAlpha(int alpha) { 823 synchronized (mService.mGlobalLock) { 824 if (mAlpha == alpha) { 825 return; 826 } 827 mAlpha = alpha; 828 invalidate(null); 829 if (DEBUG_VIEWPORT_WINDOW) { 830 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha); 831 } 832 } 833 } 834 setBounds(Region bounds)835 public void setBounds(Region bounds) { 836 synchronized (mService.mGlobalLock) { 837 if (mBounds.equals(bounds)) { 838 return; 839 } 840 mBounds.set(bounds); 841 invalidate(mDirtyRect); 842 if (DEBUG_VIEWPORT_WINDOW) { 843 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds); 844 } 845 } 846 } 847 updateSize()848 public void updateSize() { 849 synchronized (mService.mGlobalLock) { 850 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); 851 mSurfaceControl.setBufferSize(mTempPoint.x, mTempPoint.y); 852 invalidate(mDirtyRect); 853 } 854 } 855 invalidate(Rect dirtyRect)856 public void invalidate(Rect dirtyRect) { 857 if (dirtyRect != null) { 858 mDirtyRect.set(dirtyRect); 859 } else { 860 mDirtyRect.setEmpty(); 861 } 862 mInvalidated = true; 863 mService.scheduleAnimationLocked(); 864 } 865 866 /** NOTE: This has to be called within a surface transaction. */ drawIfNeeded()867 public void drawIfNeeded() { 868 synchronized (mService.mGlobalLock) { 869 if (!mInvalidated) { 870 return; 871 } 872 mInvalidated = false; 873 if (mAlpha > 0) { 874 Canvas canvas = null; 875 try { 876 // Empty dirty rectangle means unspecified. 877 if (mDirtyRect.isEmpty()) { 878 mBounds.getBounds(mDirtyRect); 879 } 880 mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth); 881 canvas = mSurface.lockCanvas(mDirtyRect); 882 if (DEBUG_VIEWPORT_WINDOW) { 883 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect); 884 } 885 } catch (IllegalArgumentException iae) { 886 /* ignore */ 887 } catch (Surface.OutOfResourcesException oore) { 888 /* ignore */ 889 } 890 if (canvas == null) { 891 return; 892 } 893 if (DEBUG_VIEWPORT_WINDOW) { 894 Slog.i(LOG_TAG, "Bounds: " + mBounds); 895 } 896 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); 897 mPaint.setAlpha(mAlpha); 898 Path path = mBounds.getBoundaryPath(); 899 canvas.drawPath(path, mPaint); 900 901 mSurface.unlockCanvasAndPost(canvas); 902 mSurfaceControl.show(); 903 } else { 904 mSurfaceControl.hide(); 905 } 906 } 907 } 908 releaseSurface()909 public void releaseSurface() { 910 mService.mTransactionFactory.make().remove(mSurfaceControl).apply(); 911 mSurface.release(); 912 } 913 914 private final class AnimationController extends Handler { 915 private static final String PROPERTY_NAME_ALPHA = "alpha"; 916 917 private static final int MIN_ALPHA = 0; 918 private static final int MAX_ALPHA = 255; 919 920 private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1; 921 922 private final ValueAnimator mShowHideFrameAnimator; 923 AnimationController(Context context, Looper looper)924 public AnimationController(Context context, Looper looper) { 925 super(looper); 926 mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this, 927 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA); 928 929 Interpolator interpolator = new DecelerateInterpolator(2.5f); 930 final long longAnimationDuration = context.getResources().getInteger( 931 com.android.internal.R.integer.config_longAnimTime); 932 933 mShowHideFrameAnimator.setInterpolator(interpolator); 934 mShowHideFrameAnimator.setDuration(longAnimationDuration); 935 } 936 onFrameShownStateChanged(boolean shown, boolean animate)937 public void onFrameShownStateChanged(boolean shown, boolean animate) { 938 obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED, 939 shown ? 1 : 0, animate ? 1 : 0).sendToTarget(); 940 } 941 942 @Override handleMessage(Message message)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; 948 949 if (animate) { 950 if (mShowHideFrameAnimator.isRunning()) { 951 mShowHideFrameAnimator.reverse(); 952 } else { 953 if (shown) { 954 mShowHideFrameAnimator.start(); 955 } else { 956 mShowHideFrameAnimator.reverse(); 957 } 958 } 959 } else { 960 mShowHideFrameAnimator.cancel(); 961 if (shown) { 962 setAlpha(MAX_ALPHA); 963 } else { 964 setAlpha(MIN_ALPHA); 965 } 966 } 967 } break; 968 } 969 } 970 } 971 } 972 } 973 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; 980 MyHandler(Looper looper)981 public MyHandler(Looper looper) { 982 super(looper); 983 } 984 985 @Override handleMessage(Message message)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(); 993 } break; 994 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); 1002 args.recycle(); 1003 } break; 1004 1005 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: { 1006 mCallbacks.onUserContextChanged(); 1007 } break; 1008 1009 case MESSAGE_NOTIFY_ROTATION_CHANGED: { 1010 final int rotation = message.arg1; 1011 mCallbacks.onRotationChanged(rotation); 1012 } break; 1013 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(); 1020 } 1021 } 1022 } break; 1023 } 1024 } 1025 } 1026 } 1027 1028 /** 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. 1032 */ 1033 private static final class WindowsForAccessibilityObserver { 1034 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? 1035 "WindowsForAccessibilityObserver" : TAG_WM; 1036 1037 private static final boolean DEBUG = false; 1038 1039 private final SparseArray<WindowState> mTempWindowStates = 1040 new SparseArray<WindowState>(); 1041 1042 private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>(); 1043 1044 private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>(); 1045 1046 private final RectF mTempRectF = new RectF(); 1047 1048 private final Matrix mTempMatrix = new Matrix(); 1049 1050 private final Point mTempPoint = new Point(); 1051 1052 private final Rect mTempRect = new Rect(); 1053 1054 private final Region mTempRegion = new Region(); 1055 1056 private final Region mTempRegion1 = new Region(); 1057 1058 private final Context mContext; 1059 1060 private final WindowManagerService mService; 1061 1062 private final Handler mHandler; 1063 1064 private final WindowsForAccessibilityCallback mCallback; 1065 1066 private final long mRecurringAccessibilityEventsIntervalMillis; 1067 WindowsForAccessibilityObserver(WindowManagerService windowManagerService, WindowsForAccessibilityCallback callback)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); 1077 } 1078 performComputeChangedWindowsNotLocked(boolean forceSend)1079 public void performComputeChangedWindowsNotLocked(boolean forceSend) { 1080 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS); 1081 computeChangedWindows(forceSend); 1082 } 1083 scheduleComputeChangedWindowsLocked()1084 public void scheduleComputeChangedWindowsLocked() { 1085 if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) { 1086 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS, 1087 mRecurringAccessibilityEventsIntervalMillis); 1088 } 1089 } 1090 1091 /** 1092 * Check if windows have changed, and send them to the accessibility subsystem if they have. 1093 * 1094 * @param forceSend Send the windows the accessibility even if they haven't changed. 1095 */ computeChangedWindows(boolean forceSend)1096 public void computeChangedWindows(boolean forceSend) { 1097 if (DEBUG) { 1098 Slog.i(LOG_TAG, "computeChangedWindows()"); 1099 } 1100 1101 boolean windowsChanged = false; 1102 List<WindowInfo> windows = new ArrayList<WindowInfo>(); 1103 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()) { 1110 return; 1111 } 1112 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; 1118 1119 Region unaccountedSpace = mTempRegion; 1120 unaccountedSpace.set(0, 0, screenWidth, screenHeight); 1121 1122 final SparseArray<WindowState> visibleWindows = mTempWindowStates; 1123 populateVisibleWindowsOnScreenLocked(visibleWindows); 1124 Set<IBinder> addedWindows = mTempBinderSet; 1125 addedWindows.clear(); 1126 1127 boolean focusedWindowAdded = false; 1128 1129 final int visibleWindowCount = visibleWindows.size(); 1130 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>(); 1131 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); 1135 1136 final Rect boundsInScreen = mTempRect; 1137 computeWindowBoundsInScreen(windowState, boundsInScreen); 1138 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(); 1145 } 1146 1147 if (unaccountedSpace.isEmpty() && focusedWindowAdded) { 1148 break; 1149 } 1150 } 1151 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; 1158 } 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); 1164 } 1165 } 1166 // Leave the child token list if empty. 1167 } 1168 } 1169 1170 visibleWindows.clear(); 1171 addedWindows.clear(); 1172 1173 if (!forceSend) { 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 1187 // to the clients. 1188 if (windowChangedNoLayer(oldWindow, newWindow)) { 1189 windowsChanged = true; 1190 break; 1191 } 1192 } 1193 } 1194 } 1195 1196 if (forceSend || windowsChanged) { 1197 cacheWindows(windows); 1198 } 1199 } 1200 1201 // Now we do not hold the lock, so send the windows over. 1202 if (forceSend || windowsChanged) { 1203 if (DEBUG) { 1204 Log.i(LOG_TAG, "Windows changed or force sending:" + windows); 1205 } 1206 mCallback.onWindowsForAccessibilityChanged(windows); 1207 } else { 1208 if (DEBUG) { 1209 Log.i(LOG_TAG, "No windows changed."); 1210 } 1211 } 1212 1213 // Recycle the windows as we do not need them. 1214 clearAndRecycleWindows(windows); 1215 } 1216 windowMattersToAccessibility(WindowState windowState, Rect boundsInScreen, Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks)1217 private boolean windowMattersToAccessibility(WindowState windowState, Rect boundsInScreen, 1218 Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) { 1219 if (windowState.isFocused()) { 1220 return true; 1221 } 1222 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)) { 1226 return false; 1227 } 1228 1229 // Ignore non-touchable windows, except the split-screen divider, which is 1230 // occasionally non-touchable but still useful for identifying split-screen 1231 // mode. 1232 if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) 1233 && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) { 1234 return false; 1235 } 1236 1237 // If the window is completely covered by other windows - ignore. 1238 if (unaccountedSpace.quickReject(boundsInScreen)) { 1239 return false; 1240 } 1241 1242 // Add windows of certain types not covered by modal windows. 1243 if (isReportedWindowType(windowState.mAttrs.type)) { 1244 return true; 1245 } 1246 1247 return false; 1248 } 1249 updateUnaccountedSpace(WindowState windowState, Rect boundsInScreen, Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks)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) { 1254 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); 1260 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 1266 // area. 1267 unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace, 1268 Region.Op.REVERSE_DIFFERENCE); 1269 1270 final Task task = windowState.getTask(); 1271 if (task != null) { 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); 1275 } else { 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(); 1279 } 1280 } 1281 } 1282 } 1283 computeWindowBoundsInScreen(WindowState windowState, Rect outBounds)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); 1290 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); 1295 1296 // Map the frame to get what appears on the screen. 1297 Matrix matrix = mTempMatrix; 1298 populateTransformationMatrixLocked(windowState, matrix); 1299 matrix.mapRect(windowFrame); 1300 1301 // Got the bounds. 1302 outBounds.set((int) windowFrame.left, (int) windowFrame.top, 1303 (int) windowFrame.right, (int) windowFrame.bottom); 1304 } 1305 addPopulatedWindowInfo( WindowState windowState, Rect boundsInScreen, List<WindowInfo> out, Set<IBinder> tokenOut)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(); 1312 out.add(window); 1313 tokenOut.add(window.token); 1314 } 1315 cacheWindows(List<WindowInfo> windows)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(); 1320 } 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)); 1325 } 1326 } 1327 windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow)1328 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) { 1329 if (oldWindow == newWindow) { 1330 return false; 1331 } 1332 if (oldWindow == null) { 1333 return true; 1334 } 1335 if (newWindow == null) { 1336 return true; 1337 } 1338 if (oldWindow.type != newWindow.type) { 1339 return true; 1340 } 1341 if (oldWindow.focused != newWindow.focused) { 1342 return true; 1343 } 1344 if (oldWindow.token == null) { 1345 if (newWindow.token != null) { 1346 return true; 1347 } 1348 } else if (!oldWindow.token.equals(newWindow.token)) { 1349 return true; 1350 } 1351 if (oldWindow.parentToken == null) { 1352 if (newWindow.parentToken != null) { 1353 return true; 1354 } 1355 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) { 1356 return true; 1357 } 1358 if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) { 1359 return true; 1360 } 1361 if (oldWindow.childTokens != null && newWindow.childTokens != null 1362 && !oldWindow.childTokens.equals(newWindow.childTokens)) { 1363 return true; 1364 } 1365 if (!TextUtils.equals(oldWindow.title, newWindow.title)) { 1366 return true; 1367 } 1368 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) { 1369 return true; 1370 } 1371 return false; 1372 } 1373 clearAndRecycleWindows(List<WindowInfo> windows)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(); 1378 } 1379 } 1380 isReportedWindowType(int windowType)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); 1392 } 1393 populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows)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); 1400 } 1401 }, false /* traverseTopToBottom */); 1402 // Insert the re-parented windows in another display on top of their parents in 1403 // default display. 1404 mService.mRoot.forAllWindows(w -> { 1405 final WindowState parentWindow = findRootDisplayParentWindow(w); 1406 if (parentWindow == null) { 1407 return; 1408 } 1409 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); 1419 } 1420 }, true /* traverseTopToBottom */); 1421 for (int i = 0; i < tempWindowStatesList.size(); i++) { 1422 outWindows.put(i, tempWindowStatesList.get(i)); 1423 } 1424 } 1425 findRootDisplayParentWindow(WindowState win)1426 private WindowState findRootDisplayParentWindow(WindowState win) { 1427 WindowState displayParentWindow = win.getDisplayContent().getParentWindow(); 1428 if (displayParentWindow == null) { 1429 return null; 1430 } 1431 WindowState candidate = displayParentWindow; 1432 while (candidate != null) { 1433 displayParentWindow = candidate; 1434 candidate = displayParentWindow.getDisplayContent().getParentWindow(); 1435 } 1436 return displayParentWindow; 1437 } 1438 isCurrentFocusWindowOnDefaultDisplay()1439 private boolean isCurrentFocusWindowOnDefaultDisplay() { 1440 final WindowState focusedWindow = 1441 mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus; 1442 if (focusedWindow == null) { 1443 return false; 1444 } 1445 1446 final WindowState rootDisplayParentWindow = findRootDisplayParentWindow(focusedWindow); 1447 if (!focusedWindow.isDefaultDisplay() 1448 && (rootDisplayParentWindow == null 1449 || !rootDisplayParentWindow.isDefaultDisplay())) { 1450 return false; 1451 } 1452 1453 return true; 1454 } 1455 1456 private class MyHandler extends Handler { 1457 public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1; 1458 MyHandler(Looper looper)1459 public MyHandler(Looper looper) { 1460 super(looper, null, false); 1461 } 1462 1463 @Override 1464 @SuppressWarnings("unchecked") handleMessage(Message message)1465 public void handleMessage(Message message) { 1466 switch (message.what) { 1467 case MESSAGE_COMPUTE_CHANGED_WINDOWS: { 1468 computeChangedWindows(false); 1469 } break; 1470 } 1471 } 1472 } 1473 } 1474 } 1475