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.TYPE_MAGNIFICATION_OVERLAY; 20 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; 21 22 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 24 25 import android.animation.ObjectAnimator; 26 import android.animation.ValueAnimator; 27 import android.annotation.NonNull; 28 import android.app.Service; 29 import android.content.Context; 30 import android.graphics.Canvas; 31 import android.graphics.Color; 32 import android.graphics.Matrix; 33 import android.graphics.Paint; 34 import android.graphics.Path; 35 import android.graphics.PixelFormat; 36 import android.graphics.Point; 37 import android.graphics.PorterDuff.Mode; 38 import android.graphics.Rect; 39 import android.graphics.RectF; 40 import android.graphics.Region; 41 import android.os.Handler; 42 import android.os.IBinder; 43 import android.os.Looper; 44 import android.os.Message; 45 import android.text.TextUtils; 46 import android.util.ArraySet; 47 import android.util.Log; 48 import android.util.Slog; 49 import android.util.SparseArray; 50 import android.util.TypedValue; 51 import android.view.MagnificationSpec; 52 import android.view.Surface; 53 import android.view.Surface.OutOfResourcesException; 54 import android.view.SurfaceControl; 55 import android.view.ViewConfiguration; 56 import android.view.WindowInfo; 57 import android.view.WindowManager; 58 import android.view.WindowManagerInternal.MagnificationCallbacks; 59 import android.view.WindowManagerInternal.WindowsForAccessibilityCallback; 60 import android.view.WindowManagerPolicy; 61 import android.view.animation.DecelerateInterpolator; 62 import android.view.animation.Interpolator; 63 64 import com.android.internal.R; 65 import com.android.internal.os.SomeArgs; 66 67 import java.util.ArrayList; 68 import java.util.HashSet; 69 import java.util.List; 70 import java.util.Set; 71 72 /** 73 * This class contains the accessibility related logic of the window manger. 74 */ 75 final class AccessibilityController { 76 77 private final WindowManagerService mWindowManagerService; 78 79 private static final float[] sTempFloats = new float[9]; 80 81 public AccessibilityController(WindowManagerService service) { 82 mWindowManagerService = service; 83 } 84 85 private DisplayMagnifier mDisplayMagnifier; 86 87 private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver; 88 89 public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) { 90 if (callbacks != null) { 91 if (mDisplayMagnifier != null) { 92 throw new IllegalStateException("Magnification callbacks already set!"); 93 } 94 mDisplayMagnifier = new DisplayMagnifier(mWindowManagerService, callbacks); 95 } else { 96 if (mDisplayMagnifier == null) { 97 throw new IllegalStateException("Magnification callbacks already cleared!"); 98 } 99 mDisplayMagnifier.destroyLocked(); 100 mDisplayMagnifier = null; 101 } 102 } 103 104 public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) { 105 if (callback != null) { 106 if (mWindowsForAccessibilityObserver != null) { 107 throw new IllegalStateException( 108 "Windows for accessibility callback already set!"); 109 } 110 mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver( 111 mWindowManagerService, callback); 112 } else { 113 if (mWindowsForAccessibilityObserver == null) { 114 throw new IllegalStateException( 115 "Windows for accessibility callback already cleared!"); 116 } 117 mWindowsForAccessibilityObserver = null; 118 } 119 } 120 121 public void performComputeChangedWindowsNotLocked() { 122 WindowsForAccessibilityObserver observer = null; 123 synchronized (mWindowManagerService) { 124 observer = mWindowsForAccessibilityObserver; 125 } 126 if (observer != null) { 127 observer.performComputeChangedWindowsNotLocked(); 128 } 129 } 130 131 public void setMagnificationSpecLocked(MagnificationSpec spec) { 132 if (mDisplayMagnifier != null) { 133 mDisplayMagnifier.setMagnificationSpecLocked(spec); 134 } 135 if (mWindowsForAccessibilityObserver != null) { 136 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 137 } 138 } 139 140 public void getMagnificationRegionLocked(Region outMagnificationRegion) { 141 if (mDisplayMagnifier != null) { 142 mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion); 143 } 144 } 145 146 public void onRectangleOnScreenRequestedLocked(Rect rectangle) { 147 if (mDisplayMagnifier != null) { 148 mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle); 149 } 150 // Not relevant for the window observer. 151 } 152 153 public void onWindowLayersChangedLocked() { 154 if (mDisplayMagnifier != null) { 155 mDisplayMagnifier.onWindowLayersChangedLocked(); 156 } 157 if (mWindowsForAccessibilityObserver != null) { 158 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 159 } 160 } 161 162 public void onRotationChangedLocked(DisplayContent displayContent) { 163 if (mDisplayMagnifier != null) { 164 mDisplayMagnifier.onRotationChangedLocked(displayContent); 165 } 166 if (mWindowsForAccessibilityObserver != null) { 167 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 168 } 169 } 170 171 public void onAppWindowTransitionLocked(WindowState windowState, int transition) { 172 if (mDisplayMagnifier != null) { 173 mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition); 174 } 175 // Not relevant for the window observer. 176 } 177 178 public void onWindowTransitionLocked(WindowState windowState, int transition) { 179 if (mDisplayMagnifier != null) { 180 mDisplayMagnifier.onWindowTransitionLocked(windowState, transition); 181 } 182 if (mWindowsForAccessibilityObserver != null) { 183 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 184 } 185 } 186 187 public void onWindowFocusChangedNotLocked() { 188 // Not relevant for the display magnifier. 189 190 WindowsForAccessibilityObserver observer = null; 191 synchronized (mWindowManagerService) { 192 observer = mWindowsForAccessibilityObserver; 193 } 194 if (observer != null) { 195 observer.performComputeChangedWindowsNotLocked(); 196 } 197 } 198 199 200 public void onSomeWindowResizedOrMovedLocked() { 201 // Not relevant for the display magnifier. 202 203 if (mWindowsForAccessibilityObserver != null) { 204 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 205 } 206 } 207 208 /** NOTE: This has to be called within a surface transaction. */ 209 public void drawMagnifiedRegionBorderIfNeededLocked() { 210 if (mDisplayMagnifier != null) { 211 mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(); 212 } 213 // Not relevant for the window observer. 214 } 215 216 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { 217 if (mDisplayMagnifier != null) { 218 return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState); 219 } 220 return null; 221 } 222 223 public boolean hasCallbacksLocked() { 224 return (mDisplayMagnifier != null 225 || mWindowsForAccessibilityObserver != null); 226 } 227 228 public void setForceShowMagnifiableBoundsLocked(boolean show) { 229 if (mDisplayMagnifier != null) { 230 mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show); 231 mDisplayMagnifier.showMagnificationBoundsIfNeeded(); 232 } 233 } 234 235 private static void populateTransformationMatrixLocked(WindowState windowState, 236 Matrix outMatrix) { 237 sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx; 238 sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx; 239 sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy; 240 sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy; 241 sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x; 242 sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y; 243 sTempFloats[Matrix.MPERSP_0] = 0; 244 sTempFloats[Matrix.MPERSP_1] = 0; 245 sTempFloats[Matrix.MPERSP_2] = 1; 246 outMatrix.setValues(sTempFloats); 247 } 248 249 /** 250 * This class encapsulates the functionality related to display magnification. 251 */ 252 private static final class DisplayMagnifier { 253 254 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM; 255 256 private static final boolean DEBUG_WINDOW_TRANSITIONS = false; 257 private static final boolean DEBUG_ROTATION = false; 258 private static final boolean DEBUG_LAYERS = false; 259 private static final boolean DEBUG_RECTANGLE_REQUESTED = false; 260 private static final boolean DEBUG_VIEWPORT_WINDOW = false; 261 262 private final Rect mTempRect1 = new Rect(); 263 private final Rect mTempRect2 = new Rect(); 264 265 private final Region mTempRegion1 = new Region(); 266 private final Region mTempRegion2 = new Region(); 267 private final Region mTempRegion3 = new Region(); 268 private final Region mTempRegion4 = new Region(); 269 270 private final Context mContext; 271 private final WindowManagerService mWindowManagerService; 272 private final MagnifiedViewport mMagnifedViewport; 273 private final Handler mHandler; 274 275 private final MagnificationCallbacks mCallbacks; 276 277 private final long mLongAnimationDuration; 278 279 private boolean mForceShowMagnifiableBounds = false; 280 281 public DisplayMagnifier(WindowManagerService windowManagerService, 282 MagnificationCallbacks callbacks) { 283 mContext = windowManagerService.mContext; 284 mWindowManagerService = windowManagerService; 285 mCallbacks = callbacks; 286 mHandler = new MyHandler(mWindowManagerService.mH.getLooper()); 287 mMagnifedViewport = new MagnifiedViewport(); 288 mLongAnimationDuration = mContext.getResources().getInteger( 289 com.android.internal.R.integer.config_longAnimTime); 290 } 291 292 public void setMagnificationSpecLocked(MagnificationSpec spec) { 293 mMagnifedViewport.updateMagnificationSpecLocked(spec); 294 mMagnifedViewport.recomputeBoundsLocked(); 295 mWindowManagerService.scheduleAnimationLocked(); 296 } 297 298 public void setForceShowMagnifiableBoundsLocked(boolean show) { 299 mForceShowMagnifiableBounds = show; 300 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true); 301 } 302 303 public boolean isForceShowingMagnifiableBoundsLocked() { 304 return mForceShowMagnifiableBounds; 305 } 306 307 public void onRectangleOnScreenRequestedLocked(Rect rectangle) { 308 if (DEBUG_RECTANGLE_REQUESTED) { 309 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); 310 } 311 if (!mMagnifedViewport.isMagnifyingLocked()) { 312 return; 313 } 314 Rect magnifiedRegionBounds = mTempRect2; 315 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds); 316 if (magnifiedRegionBounds.contains(rectangle)) { 317 return; 318 } 319 SomeArgs args = SomeArgs.obtain(); 320 args.argi1 = rectangle.left; 321 args.argi2 = rectangle.top; 322 args.argi3 = rectangle.right; 323 args.argi4 = rectangle.bottom; 324 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED, 325 args).sendToTarget(); 326 } 327 328 public void onWindowLayersChangedLocked() { 329 if (DEBUG_LAYERS) { 330 Slog.i(LOG_TAG, "Layers changed."); 331 } 332 mMagnifedViewport.recomputeBoundsLocked(); 333 mWindowManagerService.scheduleAnimationLocked(); 334 } 335 336 public void onRotationChangedLocked(DisplayContent displayContent) { 337 if (DEBUG_ROTATION) { 338 final int rotation = displayContent.getRotation(); 339 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation) 340 + " displayId: " + displayContent.getDisplayId()); 341 } 342 mMagnifedViewport.onRotationChangedLocked(); 343 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED); 344 } 345 346 public void onAppWindowTransitionLocked(WindowState windowState, int transition) { 347 if (DEBUG_WINDOW_TRANSITIONS) { 348 Slog.i(LOG_TAG, "Window transition: " 349 + AppTransition.appTransitionToString(transition) 350 + " displayId: " + windowState.getDisplayId()); 351 } 352 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); 353 if (magnifying) { 354 switch (transition) { 355 case AppTransition.TRANSIT_ACTIVITY_OPEN: 356 case AppTransition.TRANSIT_TASK_OPEN: 357 case AppTransition.TRANSIT_TASK_TO_FRONT: 358 case AppTransition.TRANSIT_WALLPAPER_OPEN: 359 case AppTransition.TRANSIT_WALLPAPER_CLOSE: 360 case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: { 361 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED); 362 } 363 } 364 } 365 } 366 367 public void onWindowTransitionLocked(WindowState windowState, int transition) { 368 if (DEBUG_WINDOW_TRANSITIONS) { 369 Slog.i(LOG_TAG, "Window transition: " 370 + AppTransition.appTransitionToString(transition) 371 + " displayId: " + windowState.getDisplayId()); 372 } 373 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); 374 final int type = windowState.mAttrs.type; 375 switch (transition) { 376 case WindowManagerPolicy.TRANSIT_ENTER: 377 case WindowManagerPolicy.TRANSIT_SHOW: { 378 if (!magnifying) { 379 break; 380 } 381 switch (type) { 382 case WindowManager.LayoutParams.TYPE_APPLICATION: 383 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION: 384 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: 385 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: 386 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: 387 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL: 388 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: 389 case WindowManager.LayoutParams.TYPE_SEARCH_BAR: 390 case WindowManager.LayoutParams.TYPE_PHONE: 391 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: 392 case WindowManager.LayoutParams.TYPE_TOAST: 393 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: 394 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY: 395 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: 396 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: 397 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: 398 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: 399 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: 400 case WindowManager.LayoutParams.TYPE_QS_DIALOG: 401 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: { 402 Rect magnifiedRegionBounds = mTempRect2; 403 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked( 404 magnifiedRegionBounds); 405 Rect touchableRegionBounds = mTempRect1; 406 windowState.getTouchableRegion(mTempRegion1); 407 mTempRegion1.getBounds(touchableRegionBounds); 408 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) { 409 mCallbacks.onRectangleOnScreenRequested( 410 touchableRegionBounds.left, 411 touchableRegionBounds.top, 412 touchableRegionBounds.right, 413 touchableRegionBounds.bottom); 414 } 415 } break; 416 } break; 417 } 418 } 419 } 420 421 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { 422 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked(); 423 if (spec != null && !spec.isNop()) { 424 if (!mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) { 425 return null; 426 } 427 } 428 return spec; 429 } 430 431 public void getMagnificationRegionLocked(Region outMagnificationRegion) { 432 // Make sure we're working with the most current bounds 433 mMagnifedViewport.recomputeBoundsLocked(); 434 mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion); 435 } 436 437 public void destroyLocked() { 438 mMagnifedViewport.destroyWindow(); 439 } 440 441 // Can be called outside of a surface transaction 442 public void showMagnificationBoundsIfNeeded() { 443 mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED) 444 .sendToTarget(); 445 } 446 447 /** NOTE: This has to be called within a surface transaction. */ 448 public void drawMagnifiedRegionBorderIfNeededLocked() { 449 mMagnifedViewport.drawWindowIfNeededLocked(); 450 } 451 452 private final class MagnifiedViewport { 453 454 private final SparseArray<WindowState> mTempWindowStates = 455 new SparseArray<WindowState>(); 456 457 private final RectF mTempRectF = new RectF(); 458 459 private final Point mTempPoint = new Point(); 460 461 private final Matrix mTempMatrix = new Matrix(); 462 463 private final Region mMagnificationRegion = new Region(); 464 private final Region mOldMagnificationRegion = new Region(); 465 466 private final Path mCircularPath; 467 468 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain(); 469 470 private final WindowManager mWindowManager; 471 472 private final float mBorderWidth; 473 private final int mHalfBorderWidth; 474 private final int mDrawBorderInset; 475 476 private final ViewportWindow mWindow; 477 478 private boolean mFullRedrawNeeded; 479 480 public MagnifiedViewport() { 481 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE); 482 mBorderWidth = mContext.getResources().getDimension( 483 com.android.internal.R.dimen.accessibility_magnification_indicator_width); 484 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2); 485 mDrawBorderInset = (int) mBorderWidth / 2; 486 mWindow = new ViewportWindow(mContext); 487 488 if (mContext.getResources().getConfiguration().isScreenRound()) { 489 mCircularPath = new Path(); 490 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); 491 final int centerXY = mTempPoint.x / 2; 492 mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW); 493 } else { 494 mCircularPath = null; 495 } 496 497 recomputeBoundsLocked(); 498 } 499 500 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) { 501 outMagnificationRegion.set(mMagnificationRegion); 502 } 503 504 public void updateMagnificationSpecLocked(MagnificationSpec spec) { 505 if (spec != null) { 506 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY); 507 } else { 508 mMagnificationSpec.clear(); 509 } 510 // If this message is pending we are in a rotation animation and do not want 511 // to show the border. We will do so when the pending message is handled. 512 if (!mHandler.hasMessages( 513 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) { 514 setMagnifiedRegionBorderShownLocked( 515 isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true); 516 } 517 } 518 519 public void recomputeBoundsLocked() { 520 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); 521 final int screenWidth = mTempPoint.x; 522 final int screenHeight = mTempPoint.y; 523 524 mMagnificationRegion.set(0, 0, 0, 0); 525 final Region availableBounds = mTempRegion1; 526 availableBounds.set(0, 0, screenWidth, screenHeight); 527 528 if (mCircularPath != null) { 529 availableBounds.setPath(mCircularPath, availableBounds); 530 } 531 532 Region nonMagnifiedBounds = mTempRegion4; 533 nonMagnifiedBounds.set(0, 0, 0, 0); 534 535 SparseArray<WindowState> visibleWindows = mTempWindowStates; 536 visibleWindows.clear(); 537 populateWindowsOnScreenLocked(visibleWindows); 538 539 final int visibleWindowCount = visibleWindows.size(); 540 for (int i = visibleWindowCount - 1; i >= 0; i--) { 541 WindowState windowState = visibleWindows.valueAt(i); 542 if ((windowState.mAttrs.type == TYPE_MAGNIFICATION_OVERLAY) 543 || ((windowState.mAttrs.privateFlags 544 & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) { 545 continue; 546 } 547 548 // Consider the touchable portion of the window 549 Matrix matrix = mTempMatrix; 550 populateTransformationMatrixLocked(windowState, matrix); 551 Region touchableRegion = mTempRegion3; 552 windowState.getTouchableRegion(touchableRegion); 553 Rect touchableFrame = mTempRect1; 554 touchableRegion.getBounds(touchableFrame); 555 RectF windowFrame = mTempRectF; 556 windowFrame.set(touchableFrame); 557 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top); 558 matrix.mapRect(windowFrame); 559 Region windowBounds = mTempRegion2; 560 windowBounds.set((int) windowFrame.left, (int) windowFrame.top, 561 (int) windowFrame.right, (int) windowFrame.bottom); 562 // Only update new regions 563 Region portionOfWindowAlreadyAccountedFor = mTempRegion3; 564 portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion); 565 portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION); 566 windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE); 567 568 if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) { 569 mMagnificationRegion.op(windowBounds, Region.Op.UNION); 570 mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT); 571 } else { 572 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION); 573 availableBounds.op(windowBounds, Region.Op.DIFFERENCE); 574 } 575 576 // Update accounted bounds 577 Region accountedBounds = mTempRegion2; 578 accountedBounds.set(mMagnificationRegion); 579 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION); 580 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT); 581 582 if (accountedBounds.isRect()) { 583 Rect accountedFrame = mTempRect1; 584 accountedBounds.getBounds(accountedFrame); 585 if (accountedFrame.width() == screenWidth 586 && accountedFrame.height() == screenHeight) { 587 break; 588 } 589 } 590 } 591 592 visibleWindows.clear(); 593 594 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset, 595 screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset, 596 Region.Op.INTERSECT); 597 598 final boolean magnifiedChanged = 599 !mOldMagnificationRegion.equals(mMagnificationRegion); 600 if (magnifiedChanged) { 601 mWindow.setBounds(mMagnificationRegion); 602 final Rect dirtyRect = mTempRect1; 603 if (mFullRedrawNeeded) { 604 mFullRedrawNeeded = false; 605 dirtyRect.set(mDrawBorderInset, mDrawBorderInset, 606 screenWidth - mDrawBorderInset, 607 screenHeight - mDrawBorderInset); 608 mWindow.invalidate(dirtyRect); 609 } else { 610 final Region dirtyRegion = mTempRegion3; 611 dirtyRegion.set(mMagnificationRegion); 612 dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION); 613 dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT); 614 dirtyRegion.getBounds(dirtyRect); 615 mWindow.invalidate(dirtyRect); 616 } 617 618 mOldMagnificationRegion.set(mMagnificationRegion); 619 final SomeArgs args = SomeArgs.obtain(); 620 args.arg1 = Region.obtain(mMagnificationRegion); 621 mHandler.obtainMessage( 622 MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args) 623 .sendToTarget(); 624 } 625 } 626 627 public void onRotationChangedLocked() { 628 // If we are showing the magnification border, hide it immediately so 629 // the user does not see strange artifacts during rotation. The screenshot 630 // used for rotation already has the border. After the rotation is complete 631 // we will show the border. 632 if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) { 633 setMagnifiedRegionBorderShownLocked(false, false); 634 final long delay = (long) (mLongAnimationDuration 635 * mWindowManagerService.getWindowAnimationScaleLocked()); 636 Message message = mHandler.obtainMessage( 637 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED); 638 mHandler.sendMessageDelayed(message, delay); 639 } 640 recomputeBoundsLocked(); 641 mWindow.updateSize(); 642 } 643 644 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) { 645 if (shown) { 646 mFullRedrawNeeded = true; 647 mOldMagnificationRegion.set(0, 0, 0, 0); 648 } 649 mWindow.setShown(shown, animate); 650 } 651 652 public void getMagnifiedFrameInContentCoordsLocked(Rect rect) { 653 MagnificationSpec spec = mMagnificationSpec; 654 mMagnificationRegion.getBounds(rect); 655 rect.offset((int) -spec.offsetX, (int) -spec.offsetY); 656 rect.scale(1.0f / spec.scale); 657 } 658 659 public boolean isMagnifyingLocked() { 660 return mMagnificationSpec.scale > 1.0f; 661 } 662 663 public MagnificationSpec getMagnificationSpecLocked() { 664 return mMagnificationSpec; 665 } 666 667 /** NOTE: This has to be called within a surface transaction. */ 668 public void drawWindowIfNeededLocked() { 669 recomputeBoundsLocked(); 670 mWindow.drawIfNeeded(); 671 } 672 673 public void destroyWindow() { 674 mWindow.releaseSurface(); 675 } 676 677 private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { 678 final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked(); 679 dc.forAllWindows((w) -> { 680 if (w.isOnScreen() && w.isVisibleLw() 681 && !w.mWinAnimator.mEnterAnimationPending) { 682 outWindows.put(w.mLayer, w); 683 } 684 }, false /* traverseTopToBottom */ ); 685 } 686 687 private final class ViewportWindow { 688 private static final String SURFACE_TITLE = "Magnification Overlay"; 689 690 private final Region mBounds = new Region(); 691 private final Rect mDirtyRect = new Rect(); 692 private final Paint mPaint = new Paint(); 693 694 private final SurfaceControl mSurfaceControl; 695 private final Surface mSurface = new Surface(); 696 697 private final AnimationController mAnimationController; 698 699 private boolean mShown; 700 private int mAlpha; 701 702 private boolean mInvalidated; 703 704 public ViewportWindow(Context context) { 705 SurfaceControl surfaceControl = null; 706 try { 707 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); 708 surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession, 709 SURFACE_TITLE, mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT, 710 SurfaceControl.HIDDEN); 711 } catch (OutOfResourcesException oore) { 712 /* ignore */ 713 } 714 mSurfaceControl = surfaceControl; 715 mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay() 716 .getLayerStack()); 717 mSurfaceControl.setLayer(mWindowManagerService.mPolicy.getWindowLayerFromTypeLw( 718 TYPE_MAGNIFICATION_OVERLAY) 719 * WindowManagerService.TYPE_LAYER_MULTIPLIER); 720 mSurfaceControl.setPosition(0, 0); 721 mSurface.copyFrom(mSurfaceControl); 722 723 mAnimationController = new AnimationController(context, 724 mWindowManagerService.mH.getLooper()); 725 726 TypedValue typedValue = new TypedValue(); 727 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight, 728 typedValue, true); 729 final int borderColor = context.getColor(typedValue.resourceId); 730 731 mPaint.setStyle(Paint.Style.STROKE); 732 mPaint.setStrokeWidth(mBorderWidth); 733 mPaint.setColor(borderColor); 734 735 mInvalidated = true; 736 } 737 738 public void setShown(boolean shown, boolean animate) { 739 synchronized (mWindowManagerService.mWindowMap) { 740 if (mShown == shown) { 741 return; 742 } 743 mShown = shown; 744 mAnimationController.onFrameShownStateChanged(shown, animate); 745 if (DEBUG_VIEWPORT_WINDOW) { 746 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown); 747 } 748 } 749 } 750 751 @SuppressWarnings("unused") 752 // Called reflectively from an animator. 753 public int getAlpha() { 754 synchronized (mWindowManagerService.mWindowMap) { 755 return mAlpha; 756 } 757 } 758 759 public void setAlpha(int alpha) { 760 synchronized (mWindowManagerService.mWindowMap) { 761 if (mAlpha == alpha) { 762 return; 763 } 764 mAlpha = alpha; 765 invalidate(null); 766 if (DEBUG_VIEWPORT_WINDOW) { 767 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha); 768 } 769 } 770 } 771 772 public void setBounds(Region bounds) { 773 synchronized (mWindowManagerService.mWindowMap) { 774 if (mBounds.equals(bounds)) { 775 return; 776 } 777 mBounds.set(bounds); 778 invalidate(mDirtyRect); 779 if (DEBUG_VIEWPORT_WINDOW) { 780 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds); 781 } 782 } 783 } 784 785 public void updateSize() { 786 synchronized (mWindowManagerService.mWindowMap) { 787 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); 788 mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y); 789 invalidate(mDirtyRect); 790 } 791 } 792 793 public void invalidate(Rect dirtyRect) { 794 if (dirtyRect != null) { 795 mDirtyRect.set(dirtyRect); 796 } else { 797 mDirtyRect.setEmpty(); 798 } 799 mInvalidated = true; 800 mWindowManagerService.scheduleAnimationLocked(); 801 } 802 803 /** NOTE: This has to be called within a surface transaction. */ 804 public void drawIfNeeded() { 805 synchronized (mWindowManagerService.mWindowMap) { 806 if (!mInvalidated) { 807 return; 808 } 809 mInvalidated = false; 810 Canvas canvas = null; 811 try { 812 // Empty dirty rectangle means unspecified. 813 if (mDirtyRect.isEmpty()) { 814 mBounds.getBounds(mDirtyRect); 815 } 816 mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth); 817 canvas = mSurface.lockCanvas(mDirtyRect); 818 if (DEBUG_VIEWPORT_WINDOW) { 819 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect); 820 } 821 } catch (IllegalArgumentException iae) { 822 /* ignore */ 823 } catch (Surface.OutOfResourcesException oore) { 824 /* ignore */ 825 } 826 if (canvas == null) { 827 return; 828 } 829 if (DEBUG_VIEWPORT_WINDOW) { 830 Slog.i(LOG_TAG, "Bounds: " + mBounds); 831 } 832 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); 833 mPaint.setAlpha(mAlpha); 834 Path path = mBounds.getBoundaryPath(); 835 canvas.drawPath(path, mPaint); 836 837 mSurface.unlockCanvasAndPost(canvas); 838 839 if (mAlpha > 0) { 840 mSurfaceControl.show(); 841 } else { 842 mSurfaceControl.hide(); 843 } 844 } 845 } 846 847 public void releaseSurface() { 848 mSurfaceControl.release(); 849 mSurface.release(); 850 } 851 852 private final class AnimationController extends Handler { 853 private static final String PROPERTY_NAME_ALPHA = "alpha"; 854 855 private static final int MIN_ALPHA = 0; 856 private static final int MAX_ALPHA = 255; 857 858 private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1; 859 860 private final ValueAnimator mShowHideFrameAnimator; 861 862 public AnimationController(Context context, Looper looper) { 863 super(looper); 864 mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this, 865 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA); 866 867 Interpolator interpolator = new DecelerateInterpolator(2.5f); 868 final long longAnimationDuration = context.getResources().getInteger( 869 com.android.internal.R.integer.config_longAnimTime); 870 871 mShowHideFrameAnimator.setInterpolator(interpolator); 872 mShowHideFrameAnimator.setDuration(longAnimationDuration); 873 } 874 875 public void onFrameShownStateChanged(boolean shown, boolean animate) { 876 obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED, 877 shown ? 1 : 0, animate ? 1 : 0).sendToTarget(); 878 } 879 880 @Override 881 public void handleMessage(Message message) { 882 switch (message.what) { 883 case MSG_FRAME_SHOWN_STATE_CHANGED: { 884 final boolean shown = message.arg1 == 1; 885 final boolean animate = message.arg2 == 1; 886 887 if (animate) { 888 if (mShowHideFrameAnimator.isRunning()) { 889 mShowHideFrameAnimator.reverse(); 890 } else { 891 if (shown) { 892 mShowHideFrameAnimator.start(); 893 } else { 894 mShowHideFrameAnimator.reverse(); 895 } 896 } 897 } else { 898 mShowHideFrameAnimator.cancel(); 899 if (shown) { 900 setAlpha(MAX_ALPHA); 901 } else { 902 setAlpha(MIN_ALPHA); 903 } 904 } 905 } break; 906 } 907 } 908 } 909 } 910 } 911 912 private class MyHandler extends Handler { 913 public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1; 914 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2; 915 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3; 916 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4; 917 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5; 918 919 public MyHandler(Looper looper) { 920 super(looper); 921 } 922 923 @Override 924 public void handleMessage(Message message) { 925 switch (message.what) { 926 case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: { 927 final SomeArgs args = (SomeArgs) message.obj; 928 final Region magnifiedBounds = (Region) args.arg1; 929 mCallbacks.onMagnificationRegionChanged(magnifiedBounds); 930 magnifiedBounds.recycle(); 931 } break; 932 933 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: { 934 SomeArgs args = (SomeArgs) message.obj; 935 final int left = args.argi1; 936 final int top = args.argi2; 937 final int right = args.argi3; 938 final int bottom = args.argi4; 939 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom); 940 args.recycle(); 941 } break; 942 943 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: { 944 mCallbacks.onUserContextChanged(); 945 } break; 946 947 case MESSAGE_NOTIFY_ROTATION_CHANGED: { 948 final int rotation = message.arg1; 949 mCallbacks.onRotationChanged(rotation); 950 } break; 951 952 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { 953 synchronized (mWindowManagerService.mWindowMap) { 954 if (mMagnifedViewport.isMagnifyingLocked() 955 || isForceShowingMagnifiableBoundsLocked()) { 956 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true); 957 mWindowManagerService.scheduleAnimationLocked(); 958 } 959 } 960 } break; 961 } 962 } 963 } 964 } 965 966 /** 967 * This class encapsulates the functionality related to computing the windows 968 * reported for accessibility purposes. These windows are all windows a sighted 969 * user can see on the screen. 970 */ 971 private static final class WindowsForAccessibilityObserver { 972 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? 973 "WindowsForAccessibilityObserver" : TAG_WM; 974 975 private static final boolean DEBUG = false; 976 977 private final SparseArray<WindowState> mTempWindowStates = 978 new SparseArray<WindowState>(); 979 980 private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>(); 981 982 private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>(); 983 984 private final RectF mTempRectF = new RectF(); 985 986 private final Matrix mTempMatrix = new Matrix(); 987 988 private final Point mTempPoint = new Point(); 989 990 private final Rect mTempRect = new Rect(); 991 992 private final Region mTempRegion = new Region(); 993 994 private final Region mTempRegion1 = new Region(); 995 996 private final Context mContext; 997 998 private final WindowManagerService mWindowManagerService; 999 1000 private final Handler mHandler; 1001 1002 private final WindowsForAccessibilityCallback mCallback; 1003 1004 private final long mRecurringAccessibilityEventsIntervalMillis; 1005 1006 public WindowsForAccessibilityObserver(WindowManagerService windowManagerService, 1007 WindowsForAccessibilityCallback callback) { 1008 mContext = windowManagerService.mContext; 1009 mWindowManagerService = windowManagerService; 1010 mCallback = callback; 1011 mHandler = new MyHandler(mWindowManagerService.mH.getLooper()); 1012 mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration 1013 .getSendRecurringAccessibilityEventsInterval(); 1014 computeChangedWindows(); 1015 } 1016 1017 public void performComputeChangedWindowsNotLocked() { 1018 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS); 1019 computeChangedWindows(); 1020 } 1021 1022 public void scheduleComputeChangedWindowsLocked() { 1023 if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) { 1024 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS, 1025 mRecurringAccessibilityEventsIntervalMillis); 1026 } 1027 } 1028 1029 public void computeChangedWindows() { 1030 if (DEBUG) { 1031 Slog.i(LOG_TAG, "computeChangedWindows()"); 1032 } 1033 1034 boolean windowsChanged = false; 1035 List<WindowInfo> windows = new ArrayList<WindowInfo>(); 1036 1037 synchronized (mWindowManagerService.mWindowMap) { 1038 // Do not send the windows if there is no current focus as 1039 // the window manager is still looking for where to put it. 1040 // We will do the work when we get a focus change callback. 1041 if (mWindowManagerService.mCurrentFocus == null) { 1042 return; 1043 } 1044 1045 WindowManager windowManager = (WindowManager) 1046 mContext.getSystemService(Context.WINDOW_SERVICE); 1047 windowManager.getDefaultDisplay().getRealSize(mTempPoint); 1048 final int screenWidth = mTempPoint.x; 1049 final int screenHeight = mTempPoint.y; 1050 1051 Region unaccountedSpace = mTempRegion; 1052 unaccountedSpace.set(0, 0, screenWidth, screenHeight); 1053 1054 final SparseArray<WindowState> visibleWindows = mTempWindowStates; 1055 populateVisibleWindowsOnScreenLocked(visibleWindows); 1056 Set<IBinder> addedWindows = mTempBinderSet; 1057 addedWindows.clear(); 1058 1059 boolean focusedWindowAdded = false; 1060 1061 final int visibleWindowCount = visibleWindows.size(); 1062 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>(); 1063 for (int i = visibleWindowCount - 1; i >= 0; i--) { 1064 final WindowState windowState = visibleWindows.valueAt(i); 1065 final int flags = windowState.mAttrs.flags; 1066 final Task task = windowState.getTask(); 1067 1068 // If the window is part of a task that we're finished with - ignore. 1069 if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) { 1070 continue; 1071 } 1072 1073 // If the window is not touchable - ignore. 1074 if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { 1075 continue; 1076 } 1077 1078 // Compute the bounds in the screen. 1079 final Rect boundsInScreen = mTempRect; 1080 computeWindowBoundsInScreen(windowState, boundsInScreen); 1081 1082 // If the window is completely covered by other windows - ignore. 1083 if (unaccountedSpace.quickReject(boundsInScreen)) { 1084 continue; 1085 } 1086 1087 // Add windows of certain types not covered by modal windows. 1088 if (isReportedWindowType(windowState.mAttrs.type)) { 1089 // Add the window to the ones to be reported. 1090 WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen); 1091 addedWindows.add(window.token); 1092 windows.add(window); 1093 if (windowState.isFocused()) { 1094 focusedWindowAdded = true; 1095 } 1096 } 1097 1098 // Account for the space this window takes if the window 1099 // is not an accessibility overlay which does not change 1100 // the reported windows. 1101 if (windowState.mAttrs.type != 1102 WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { 1103 unaccountedSpace.op(boundsInScreen, unaccountedSpace, 1104 Region.Op.REVERSE_DIFFERENCE); 1105 } 1106 1107 // If a window is modal it prevents other windows from being touched 1108 if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1109 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) { 1110 // Account for all space in the task, whether the windows in it are 1111 // touchable or not. The modal window blocks all touches from the task's 1112 // area. 1113 unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace, 1114 Region.Op.REVERSE_DIFFERENCE); 1115 1116 if (task != null) { 1117 // If the window is associated with a particular task, we can skip the 1118 // rest of the windows for that task. 1119 skipRemainingWindowsForTasks.add(task.mTaskId); 1120 continue; 1121 } else { 1122 // If the window is not associated with a particular task, then it is 1123 // globally modal. In this case we can skip all remaining windows. 1124 break; 1125 } 1126 } 1127 // We figured out what is touchable for the entire screen - done. 1128 if (unaccountedSpace.isEmpty()) { 1129 break; 1130 } 1131 } 1132 1133 // Always report the focused window. 1134 if (!focusedWindowAdded) { 1135 for (int i = visibleWindowCount - 1; i >= 0; i--) { 1136 WindowState windowState = visibleWindows.valueAt(i); 1137 if (windowState.isFocused()) { 1138 // Compute the bounds in the screen. 1139 Rect boundsInScreen = mTempRect; 1140 computeWindowBoundsInScreen(windowState, boundsInScreen); 1141 1142 // Add the window to the ones to be reported. 1143 WindowInfo window = obtainPopulatedWindowInfo(windowState, 1144 boundsInScreen); 1145 addedWindows.add(window.token); 1146 windows.add(window); 1147 break; 1148 } 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 // We computed the windows and if they changed notify the client. 1174 if (mOldWindows.size() != windows.size()) { 1175 // Different size means something changed. 1176 windowsChanged = true; 1177 } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) { 1178 // Since we always traverse windows from high to low layer 1179 // the old and new windows at the same index should be the 1180 // same, otherwise something changed. 1181 for (int i = 0; i < windowCount; i++) { 1182 WindowInfo oldWindow = mOldWindows.get(i); 1183 WindowInfo newWindow = windows.get(i); 1184 // We do not care for layer changes given the window 1185 // order does not change. This brings no new information 1186 // to the clients. 1187 if (windowChangedNoLayer(oldWindow, newWindow)) { 1188 windowsChanged = true; 1189 break; 1190 } 1191 } 1192 } 1193 1194 if (windowsChanged) { 1195 cacheWindows(windows); 1196 } 1197 } 1198 1199 // Now we do not hold the lock, so send the windows over. 1200 if (windowsChanged) { 1201 if (DEBUG) { 1202 Log.i(LOG_TAG, "Windows changed:" + windows); 1203 } 1204 mCallback.onWindowsForAccessibilityChanged(windows); 1205 } else { 1206 if (DEBUG) { 1207 Log.i(LOG_TAG, "No windows changed."); 1208 } 1209 } 1210 1211 // Recycle the windows as we do not need them. 1212 clearAndRecycleWindows(windows); 1213 } 1214 1215 private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) { 1216 // Get the touchable frame. 1217 Region touchableRegion = mTempRegion1; 1218 windowState.getTouchableRegion(touchableRegion); 1219 Rect touchableFrame = mTempRect; 1220 touchableRegion.getBounds(touchableFrame); 1221 1222 // Move to origin as all transforms are captured by the matrix. 1223 RectF windowFrame = mTempRectF; 1224 windowFrame.set(touchableFrame); 1225 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top); 1226 1227 // Map the frame to get what appears on the screen. 1228 Matrix matrix = mTempMatrix; 1229 populateTransformationMatrixLocked(windowState, matrix); 1230 matrix.mapRect(windowFrame); 1231 1232 // Got the bounds. 1233 outBounds.set((int) windowFrame.left, (int) windowFrame.top, 1234 (int) windowFrame.right, (int) windowFrame.bottom); 1235 } 1236 1237 private static WindowInfo obtainPopulatedWindowInfo( 1238 WindowState windowState, Rect boundsInScreen) { 1239 final WindowInfo window = windowState.getWindowInfo(); 1240 window.boundsInScreen.set(boundsInScreen); 1241 return window; 1242 } 1243 1244 private void cacheWindows(List<WindowInfo> windows) { 1245 final int oldWindowCount = mOldWindows.size(); 1246 for (int i = oldWindowCount - 1; i >= 0; i--) { 1247 mOldWindows.remove(i).recycle(); 1248 } 1249 final int newWindowCount = windows.size(); 1250 for (int i = 0; i < newWindowCount; i++) { 1251 WindowInfo newWindow = windows.get(i); 1252 mOldWindows.add(WindowInfo.obtain(newWindow)); 1253 } 1254 } 1255 1256 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) { 1257 if (oldWindow == newWindow) { 1258 return false; 1259 } 1260 if (oldWindow == null) { 1261 return true; 1262 } 1263 if (newWindow == null) { 1264 return true; 1265 } 1266 if (oldWindow.type != newWindow.type) { 1267 return true; 1268 } 1269 if (oldWindow.focused != newWindow.focused) { 1270 return true; 1271 } 1272 if (oldWindow.token == null) { 1273 if (newWindow.token != null) { 1274 return true; 1275 } 1276 } else if (!oldWindow.token.equals(newWindow.token)) { 1277 return true; 1278 } 1279 if (oldWindow.parentToken == null) { 1280 if (newWindow.parentToken != null) { 1281 return true; 1282 } 1283 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) { 1284 return true; 1285 } 1286 if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) { 1287 return true; 1288 } 1289 if (oldWindow.childTokens != null && newWindow.childTokens != null 1290 && !oldWindow.childTokens.equals(newWindow.childTokens)) { 1291 return true; 1292 } 1293 if (!TextUtils.equals(oldWindow.title, newWindow.title)) { 1294 return true; 1295 } 1296 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) { 1297 return true; 1298 } 1299 return false; 1300 } 1301 1302 private static void clearAndRecycleWindows(List<WindowInfo> windows) { 1303 final int windowCount = windows.size(); 1304 for (int i = windowCount - 1; i >= 0; i--) { 1305 windows.remove(i).recycle(); 1306 } 1307 } 1308 1309 private static boolean isReportedWindowType(int windowType) { 1310 return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER 1311 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS 1312 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY 1313 && windowType != WindowManager.LayoutParams.TYPE_DRAG 1314 && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER 1315 && windowType != WindowManager.LayoutParams.TYPE_POINTER 1316 && windowType != TYPE_MAGNIFICATION_OVERLAY 1317 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY 1318 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY 1319 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION); 1320 } 1321 1322 private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { 1323 final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked(); 1324 dc.forAllWindows((w) -> { 1325 if (w.isVisibleLw()) { 1326 outWindows.put(w.mLayer, w); 1327 } 1328 }, false /* traverseTopToBottom */ ); 1329 } 1330 1331 private class MyHandler extends Handler { 1332 public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1; 1333 1334 public MyHandler(Looper looper) { 1335 super(looper, null, false); 1336 } 1337 1338 @Override 1339 @SuppressWarnings("unchecked") 1340 public void handleMessage(Message message) { 1341 switch (message.what) { 1342 case MESSAGE_COMPUTE_CHANGED_WINDOWS: { 1343 computeChangedWindows(); 1344 } break; 1345 } 1346 } 1347 } 1348 } 1349 } 1350